]> git.sesse.net Git - casparcg/commitdiff
Added support for more than 2 audio channels
authorHelge Norberg <helge.norberg@svt.se>
Tue, 30 Apr 2013 15:05:45 +0000 (17:05 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Tue, 30 Apr 2013 15:05:45 +0000 (17:05 +0200)
#61

41 files changed:
common/common.vcxproj
common/common.vcxproj.filters
common/compiler/vs/disable_silly_warnings.h
common/memory/safe_ptr.h
common/utility/iterator.h [new file with mode: 0644]
core/consumer/frame_consumer.cpp
core/core.vcxproj
core/core.vcxproj.filters
core/mixer/audio/audio_mixer.cpp
core/mixer/audio/audio_mixer.h
core/mixer/audio/audio_util.cpp [new file with mode: 0644]
core/mixer/audio/audio_util.h
core/mixer/mixer.cpp
core/mixer/mixer.h
core/mixer/read_frame.cpp
core/mixer/read_frame.h
core/mixer/write_frame.cpp
core/mixer/write_frame.h
core/producer/frame/frame_factory.h
core/thumbnail_generator.cpp
core/video_channel.cpp
core/video_channel.h
core/video_format.cpp
core/video_format.h
modules/bluefish/consumer/bluefish_consumer.cpp
modules/decklink/consumer/decklink_consumer.cpp
modules/decklink/decklink.vcxproj
modules/decklink/producer/decklink_producer.cpp
modules/ffmpeg/consumer/ffmpeg_consumer.cpp
modules/ffmpeg/producer/audio/audio_decoder.cpp
modules/ffmpeg/producer/audio/audio_decoder.h
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/muxer/frame_muxer.cpp
modules/ffmpeg/producer/muxer/frame_muxer.h
modules/ffmpeg/producer/util/util.cpp
modules/ffmpeg/producer/util/util.h
modules/flash/flash.cpp
modules/oal/consumer/oal_consumer.cpp
protocol/amcp/AMCPCommandsImpl.cpp
shell/casparcg.config
shell/server.cpp

index 4b9a133ccdbe27490ec4465d95505873844b76ad..c6109a50ff9bbd40356203e0830c18fe65579898 100644 (file)
     <ClInclude Include="stdafx.h" />\r
     <ClInclude Include="utility\assert.h" />\r
     <ClInclude Include="utility\base64.h" />\r
+    <ClInclude Include="utility\iterator.h" />\r
     <ClInclude Include="utility\move_on_copy.h" />\r
     <ClInclude Include="utility\param.h" />\r
     <ClInclude Include="utility\string.h" />\r
index ee858295a192e6d7de84297b6e0a7981bbe89c12..3eb3745663dd90e875a8b2c987bfc21ca8872313 100644 (file)
     <ClInclude Include="utility\base64.h">\r
       <Filter>source\utility</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="utility\iterator.h">\r
+      <Filter>source\utility</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 492b9283938fd58b5c8c9827d0bfc412e823071a..416ba63cdf3a4d81dc57dfc6bc8be1f15c23a8bf 100644 (file)
@@ -31,5 +31,7 @@
 #pragma warning (disable : 4714) // marked as __forceinline not inlined\r
 #pragma warning (disable : 4505) //  unreferenced local function has been removed\r
 #pragma warning (disable : 4481) //  nonstandard extension used: override specifier 'override'\r
+#pragma warning (disable : 4996) // Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct\r
+\r
 #endif\r
 \r
index 227af65a7d9d2ad654a5db088271e071084d859d..d42135f9bc535d2900facb14a2a69d149526c03c 100644 (file)
@@ -421,6 +421,12 @@ safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&&
        return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5), std::forward<P6>(p6)));\r
 }\r
 \r
+template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>\r
+safe_ptr<T> make_safe(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7)\r
+{\r
+       return safe_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5), std::forward<P6>(p6), std::forward<P7>(p7)));\r
+}\r
+\r
 template<typename T>\r
 safe_ptr<T>::safe_ptr() \r
     : p_(make_safe<T>())\r
diff --git a/common/utility/iterator.h b/common/utility/iterator.h
new file mode 100644 (file)
index 0000000..5ee4584
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <boost/iterator/iterator_facade.hpp>
+
+namespace caspar {
+
+/**
+ * An iterator that will automatically skip elements based on the NextFinder
+ * policy.
+ */
+template<typename Value, typename NextFinder, typename Iter = Value*>
+class position_based_skip_iterator : public boost::iterator_facade<
+               position_based_skip_iterator<Value, NextFinder, Iter>,
+               Value,
+               boost::forward_traversal_tag>
+{
+       Iter pos_;
+       Iter end_;
+       NextFinder next_finder_;
+public:
+       position_based_skip_iterator()
+       {
+       }
+
+       position_based_skip_iterator(const Iter& position)
+               : pos_(position)
+       {
+       }
+
+       position_based_skip_iterator(
+                       const Iter& position, const Iter& end, const NextFinder& next_finder)
+               : pos_(position), end_(end), next_finder_(next_finder)
+       {
+       }
+private:
+       friend class boost::iterator_core_access;
+
+       void increment()
+       {
+               pos_ = next_finder_.next(pos_, end_);
+       }
+
+       bool equal(const position_based_skip_iterator<Value, NextFinder, Iter>& other) const
+       {
+               return pos_ == other.pos_;
+       }
+
+       Value& dereference() const
+       {
+               return *pos_;
+       }
+};
+
+/**
+ * Simple NextFinder policy class which always steps a given distance.
+ */
+class constant_step_finder
+{
+       std::ptrdiff_t step_;
+       size_t num_steps_;
+       mutable size_t steps_made_;
+public:
+       constant_step_finder()
+       {
+       }
+
+       constant_step_finder(std::ptrdiff_t step, size_t num_steps)
+               : step_(step)
+               , num_steps_(num_steps)
+               , steps_made_(0)
+       {
+       }
+
+       template<typename Iter>
+       Iter next(const Iter& relative_to, const Iter& end) const
+       {
+               if (steps_made_ + 1 > num_steps_)
+                       return end;
+
+               ++steps_made_;
+
+               return relative_to + step_;
+       }
+};
+
+}
index a1e35e29e9627e76506308e927d4448822f599c8..251b3dcb2f8da1484ad25e661ce3c707856be5ce 100644 (file)
@@ -94,7 +94,7 @@ public:
 \r
                boost::unique_future<bool> result = caspar::wrap_as_future(true);\r
                \r
-               if(boost::range::equal(sync_buffer_, audio_cadence_) && audio_cadence_.front() * format_desc_.audio_channels == static_cast<size_t>(frame->audio_data().size()))\r
+               if(boost::range::equal(sync_buffer_, audio_cadence_) && audio_cadence_.front() * frame->num_channels() == static_cast<size_t>(frame->audio_data().size()))\r
                {       \r
                        // Audio sent so far is in sync, now we can send the next chunk.\r
                        result = consumer_->send(frame);\r
@@ -103,7 +103,7 @@ public:
                else\r
                        CASPAR_LOG(trace) << print() << L" Syncing audio.";\r
 \r
-               sync_buffer_.push_back(static_cast<size_t>(frame->audio_data().size() / format_desc_.audio_channels));\r
+               sync_buffer_.push_back(static_cast<size_t>(frame->audio_data().size() / frame->num_channels()));\r
                \r
                return std::move(result);\r
        }\r
index fbd0729d95570fff53c4b4ad2215b77d6849dabd..da3abde537642b262c6618196b54f33e815eb0e4 100644 (file)
     <ClInclude Include="StdAfx.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClCompile Include="mixer\audio\audio_util.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="mixer\gpu\fence.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
index 0fb5e67d1d3cc86c70c8d72c282d8a5d1d0731fa..93dfaa7910b5783cbc71ef9df997da32f1ae94c5 100644 (file)
     <ClCompile Include="monitor\monitor.cpp">\r
       <Filter>source\monitor</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="mixer\audio\audio_util.cpp">\r
+      <Filter>source\mixer\audio</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index c04f1592a5dc6d5ee8ec1bb2e7fe5b81a79a889d..7ebd70735a09ba29961583f8d133f27cb23b3b62 100644 (file)
@@ -26,6 +26,7 @@
 #include <core/mixer/write_frame.h>\r
 #include <core/producer/frame/frame_transform.h>\r
 #include <common/diagnostics/graph.h>\r
+#include "audio_util.h"\r
 \r
 #include <tbb/cache_aligned_allocator.h>\r
 \r
@@ -60,8 +61,8 @@ typedef std::vector<float, tbb::cache_aligned_allocator<float>> audio_buffer_ps;
        \r
 struct audio_stream\r
 {\r
-       frame_transform         prev_transform;\r
-       audio_buffer_ps         audio_data;\r
+       frame_transform prev_transform;\r
+       audio_buffer_ps audio_data;\r
 };\r
 \r
 struct audio_mixer::implementation\r
@@ -72,6 +73,7 @@ struct audio_mixer::implementation
        std::vector<audio_item>                         items_;\r
        std::vector<size_t>                                     audio_cadence_;\r
        video_format_desc                                       format_desc_;\r
+       channel_layout                                          channel_layout_;\r
        float                                                           master_volume_;\r
        float                                                           previous_master_volume_;\r
        \r
@@ -99,7 +101,34 @@ public:
                audio_item item;\r
                item.tag                = frame.tag();\r
                item.transform  = transform_stack_.top();\r
-               item.audio_data = std::move(frame.audio_data()); // Note: We don't need to care about upper/lower since audio_data is removed/moved from the last field.\r
+\r
+               if (needs_rearranging(frame.get_channel_layout(), channel_layout_))\r
+               {\r
+                       auto src_view = frame.get_multichannel_view();\r
+                       \r
+                       audio_buffer rearranged_buffer;\r
+                       rearranged_buffer.resize(\r
+                                       src_view.num_samples() * channel_layout_.num_channels);\r
+\r
+                       auto dst_view = make_multichannel_view<int32_t>(\r
+                                       rearranged_buffer.begin(),\r
+                                       rearranged_buffer.end(),\r
+                                       channel_layout_);\r
+\r
+                       bool rearrange_success = rearrange_or_rearrange_and_mix(\r
+                                       src_view, dst_view, default_mix_config_repository());\r
+\r
+                       if (!rearrange_success)\r
+                       {\r
+                               failed_rearrange(item.tag, src_view.channel_layout());\r
+                       }\r
+\r
+                       item.audio_data = std::move(rearranged_buffer);\r
+               }\r
+               else\r
+               {\r
+                       item.audio_data = std::move(frame.audio_data()); // Note: We don't need to care about upper/lower since audio_data is removed/moved from the last field.\r
+               }\r
                \r
                items_.push_back(std::move(item));              \r
        }\r
@@ -119,14 +148,15 @@ public:
                master_volume_ = volume;\r
        }\r
        \r
-       audio_buffer mix(const video_format_desc& format_desc)\r
+       audio_buffer mix(const video_format_desc& format_desc, const channel_layout& layout)\r
        {       \r
                if(format_desc_ != format_desc)\r
                {\r
                        audio_streams_.clear();\r
                        audio_cadence_ = format_desc.audio_cadence;\r
                        format_desc_ = format_desc;\r
-               }               \r
+                       channel_layout_ = layout;\r
+               }\r
                \r
                std::map<const void*, audio_stream>     next_audio_streams;\r
 \r
@@ -150,11 +180,11 @@ public:
                        const float prev_volume = static_cast<float>(prev_transform.volume) * previous_master_volume_;\r
                        const float next_volume = static_cast<float>(next_transform.volume) * master_volume_;\r
                                                                        \r
-                       auto alpha = (next_volume-prev_volume)/static_cast<float>(item.audio_data.size()/format_desc.audio_channels);\r
+                       auto alpha = (next_volume-prev_volume)/static_cast<float>(item.audio_data.size()/channel_layout_.num_channels);\r
                        \r
                        for(size_t n = 0; n < item.audio_data.size(); ++n)\r
                        {\r
-                               auto sample_multiplier = (prev_volume + (n/format_desc_.audio_channels) * alpha);\r
+                               auto sample_multiplier = (prev_volume + (n/channel_layout_.num_channels) * alpha);\r
                                next_audio.push_back(item.audio_data[n] * sample_multiplier);\r
                        }\r
                                                                                \r
@@ -212,7 +242,20 @@ public:
 \r
        size_t audio_size(size_t num_samples) const\r
        {\r
-               return num_samples * format_desc_.audio_channels;\r
+               return num_samples * channel_layout_.num_channels;\r
+       }\r
+\r
+       void failed_rearrange(const void* tag, const channel_layout& layout)\r
+       {\r
+               if (audio_streams_.find(tag) != audio_streams_.end())\r
+                       return; // We don't want to flood the logs.\r
+\r
+               CASPAR_LOG(warning)\r
+                               << L"[audio_mixer] Could not satisfactory down/upmix from " \r
+                               << layout.name << L" to " << channel_layout_.name \r
+                               << L" because no mix config was found for " \r
+                               << layout.layout_type << L" => " << channel_layout_.layout_type \r
+                               << L". This might cause audio to be lost.";\r
        }\r
 };\r
 \r
@@ -221,6 +264,6 @@ void audio_mixer::begin(core::basic_frame& frame){impl_->begin(frame);}
 void audio_mixer::visit(core::write_frame& frame){impl_->visit(frame);}\r
 void audio_mixer::end(){impl_->end();}\r
 void audio_mixer::set_master_volume(float volume) { impl_->set_master_volume(volume); }\r
-audio_buffer audio_mixer::operator()(const video_format_desc& format_desc){return impl_->mix(format_desc);}\r
+audio_buffer audio_mixer::operator()(const video_format_desc& format_desc, const channel_layout& layout){return impl_->mix(format_desc, layout);}\r
 \r
 }}
\ No newline at end of file
index 38134f971def23a9d620164b1ed6ba2984f6ffc5..a3196cf66fe527f4eda33e7f2eadb5df6c6bba17 100644 (file)
@@ -42,6 +42,7 @@ class graph;
 namespace core {\r
 \r
 struct video_format_desc;\r
+struct channel_layout;\r
        \r
 typedef std::vector<int32_t, tbb::cache_aligned_allocator<int32_t>> audio_buffer;\r
 \r
@@ -56,7 +57,7 @@ public:
 \r
        void set_master_volume(float volume);\r
 \r
-       audio_buffer operator()(const video_format_desc& format_desc);\r
+       audio_buffer operator()(const video_format_desc& format_desc, const channel_layout& layout);\r
        \r
 private:\r
        struct implementation;\r
diff --git a/core/mixer/audio/audio_util.cpp b/core/mixer/audio/audio_util.cpp
new file mode 100644 (file)
index 0000000..74d44e5
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "../../stdafx.h"
+
+#include "audio_util.h"
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/assign.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/property_tree/exceptions.hpp>
+
+namespace caspar { namespace core {
+
+bool channel_layout::operator==(const channel_layout& other) const
+{
+       return channel_names == other.channel_names
+                       && num_channels == other.num_channels;
+}
+
+int channel_layout::channel_index(const std::wstring& channel_name) const
+{
+       auto iter =
+                       std::find(channel_names.begin(), channel_names.end(), channel_name);
+
+       if (iter == channel_names.end())
+               return -1;
+
+       return iter - channel_names.begin();
+}
+
+bool channel_layout::has_channel(const std::wstring& channel_name) const
+{
+       return channel_index(channel_name) != -1;
+}
+
+bool channel_layout::no_channel_names() const
+{
+       return channel_names.empty();
+}
+
+bool needs_rearranging(
+               const channel_layout& source, const channel_layout& destination)
+{
+       if ((source.no_channel_names() || destination.no_channel_names())
+                       && source.num_channels == destination.num_channels)
+               return false;
+
+       return !(source == destination);
+}
+
+struct channel_layout_repository::impl
+{
+       std::map<std::wstring, const channel_layout> layouts;
+       boost::mutex mutex;
+};
+
+channel_layout_repository::channel_layout_repository()
+       : impl_(new impl)
+{
+}
+
+channel_layout_repository::~channel_layout_repository()
+{
+}
+
+void channel_layout_repository::register_layout(const channel_layout& layout)
+{
+       boost::unique_lock<boost::mutex> lock(impl_->mutex);
+
+       impl_->layouts.erase(layout.name);
+       impl_->layouts.insert(std::make_pair(layout.name, layout));
+}
+
+const channel_layout& channel_layout_repository::get_by_name(
+               const std::wstring& layout_name) const
+{
+       boost::unique_lock<boost::mutex> lock(impl_->mutex);
+
+       auto iter = impl_->layouts.find(layout_name);
+
+       if (iter == impl_->layouts.end())
+               BOOST_THROW_EXCEPTION(invalid_argument()
+                               << msg_info(narrow(layout_name) + " not found"));
+
+       return iter->second;
+}
+
+channel_layout create_layout_from_string(
+               const std::wstring& name,
+               const std::wstring& layout_type,
+               int num_channels,
+               const std::wstring& channels)
+{
+       channel_layout layout;
+
+       layout.name = boost::to_upper_copy(name);
+       layout.layout_type = boost::to_upper_copy(layout_type);
+       auto upper_channels = boost::to_upper_copy(channels);
+       
+       if (channels.length() > 0)
+               boost::split(
+                               layout.channel_names,
+                               upper_channels,
+                               boost::is_any_of(L"\t "),
+                               boost::token_compress_on);
+
+       layout.num_channels = num_channels == -1
+                       ? layout.channel_names.size() : num_channels;
+
+       return layout;
+}
+
+channel_layout create_unspecified_layout(int num_channels)
+{
+       channel_layout layout;
+
+       layout.name = L"UNORDERED" + boost::lexical_cast<std::wstring>(
+                       num_channels) + L"CH";
+       layout.layout_type = L"UNORDERED";
+       layout.num_channels = num_channels;
+
+       return layout;
+}
+
+void register_default_channel_layouts(channel_layout_repository& repository)
+{
+       repository.register_layout(create_layout_from_string(
+                       L"mono",         L"1.0",           1, L"C"));
+       repository.register_layout(create_layout_from_string(
+                       L"stereo",       L"2.0",           2, L"L R"));
+       repository.register_layout(create_layout_from_string(
+                       L"dts",          L"5.1",           6, L"C L R Ls Rs LFE"));
+       repository.register_layout(create_layout_from_string(
+                       L"dolbye",       L"5.1+stereomix", 8, L"L R C LFE Ls Rs Lmix Rmix"
+       ));
+       repository.register_layout(create_layout_from_string(
+                       L"dolbydigital", L"5.1",           6, L"L C R Ls Rs LFE"));
+       repository.register_layout(create_layout_from_string(
+                       L"smpte",        L"5.1",           6, L"L R C LFE Ls Rs"));
+       repository.register_layout(create_layout_from_string(
+                       L"passthru",     L"16ch",         16, L""));
+}
+
+void parse_channel_layouts(
+               channel_layout_repository& repository,
+               const boost::property_tree::wptree& layouts_element)
+{
+       BOOST_FOREACH(auto& layout, layouts_element)
+       {
+               repository.register_layout(create_layout_from_string(
+                               layout.first,
+                               layout.second.get<std::wstring>(L"type"),
+                               layout.second.get<int>(L"num-channels"),
+                               layout.second.get<std::wstring>(L"channels")));
+       }
+}
+
+channel_layout_repository& default_channel_layout_repository()
+{
+       static channel_layout_repository repository;
+
+       return repository;
+}
+
+struct mix_config_repository::impl
+{
+       std::map<std::wstring, std::map<std::wstring, const mix_config>> configs;
+       boost::mutex mutex;
+};
+
+mix_config_repository::mix_config_repository()
+       : impl_(new impl)
+{
+}
+
+mix_config_repository::~mix_config_repository()
+{
+}
+
+void mix_config_repository::register_mix_config(const mix_config& config)
+{
+       boost::unique_lock<boost::mutex> lock(impl_->mutex);
+
+       impl_->configs[config.from_layout_type].erase(config.to_layout_type);
+       impl_->configs[config.from_layout_type].insert(
+                       std::make_pair(config.to_layout_type, config));
+}
+
+boost::optional<mix_config> mix_config_repository::get_mix_config(
+               const std::wstring& from_layout_type,
+               const std::wstring& to_layout_type) const
+{
+       boost::unique_lock<boost::mutex> lock(impl_->mutex);
+
+       auto iter = impl_->configs[from_layout_type].find(to_layout_type);
+
+       if (iter == impl_->configs[from_layout_type].end())
+               return boost::optional<mix_config>();
+
+       return iter->second;
+}
+
+mix_config create_mix_config_from_string(
+               const std::wstring& from_layout_type,
+               const std::wstring& to_layout_type,
+               mix_config::mix_strategy strategy,
+               const std::vector<std::wstring>& mappings)
+{
+       mix_config config;
+       config.from_layout_type = boost::to_upper_copy(from_layout_type);
+       config.to_layout_type = boost::to_upper_copy(to_layout_type);
+       config.strategy = strategy;
+
+       BOOST_FOREACH(auto& mapping, mappings)
+       {
+               auto upper_mapping = boost::to_upper_copy(mapping);
+               std::vector<std::wstring> words;
+               boost::split(
+                               words,
+                               upper_mapping,
+                               boost::is_any_of(L"\t "),
+                               boost::token_compress_on);
+
+               if (words.size() != 3)
+                       BOOST_THROW_EXCEPTION(invalid_argument() << msg_info(
+                                       "mix_config mapping string must have 3 tokens"));
+
+               auto from = words.at(0);
+               auto to = words.at(1);
+               auto influence = boost::lexical_cast<double>(words.at(2));
+
+               config.destination_ch_by_source_ch.insert(std::make_pair(
+                               from,
+                               mix_config::destination(to, influence)));
+       }
+
+       return config;
+}
+
+void register_default_mix_configs(mix_config_repository& repository)
+{
+       using namespace boost::assign;
+
+       // From 1.0
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"1.0", L"2.0", mix_config::add, list_of
+                                       (L"C L 1.0")
+                                       (L"C R 1.0")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"1.0", L"5.1", mix_config::add, list_of
+                                       (L"C L 1.0")
+                                       (L"C R 1.0")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"1.0", L"5.1+stereomix", mix_config::add, list_of
+                                       (L"C L    1.0")
+                                       (L"C R    1.0")
+                                       (L"C Lmix 1.0")
+                                       (L"C Rmix 1.0")
+       ));
+       // From 2.0
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"2.0", L"1.0", mix_config::add, list_of
+                                       (L"L C 1.0")
+                                       (L"R C 1.0")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"2.0", L"5.1", mix_config::add, list_of
+                                       (L"L L    1.0")
+                                       (L"R R    1.0")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"2.0", L"5.1+stereomix", mix_config::add, list_of
+                                       (L"L L    1.0")
+                                       (L"R R    1.0")
+                                       (L"L Lmix 1.0")
+                                       (L"R Rmix 1.0")
+       ));
+       // From 5.1
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"5.1", L"1.0", mix_config::average, list_of
+                                       (L"L  C 1.0")
+                                       (L"R  C 1.0")
+                                       (L"C  C 0.707")
+                                       (L"Ls C 0.707")
+                                       (L"Rs C 0.707")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"5.1", L"2.0", mix_config::average, list_of
+                                       (L"L  L 1.0")
+                                       (L"R  R 1.0")
+                                       (L"C  L 0.707")
+                                       (L"C  R 0.707")
+                                       (L"Ls L 0.707")
+                                       (L"Rs R 0.707")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"5.1", L"5.1+stereomix", mix_config::average, list_of
+                                       (L"L   L   1.0")
+                                       (L"R   R   1.0")
+                                       (L"C   C   1.0")
+                                       (L"Ls  Ls  1.0")
+                                       (L"Rs  Rs  1.0")
+                                       (L"LFE LFE 1.0")
+
+                                       (L"L  Lmix 1.0")
+                                       (L"R  Rmix 1.0")
+                                       (L"C  Lmix 0.707")
+                                       (L"C  Rmix 0.707")
+                                       (L"Ls Lmix 0.707")
+                                       (L"Rs Rmix 0.707")
+       ));
+       // From 5.1+stereomix
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"5.1+stereomix", L"1.0", mix_config::add, list_of
+                                       (L"Lmix C 1.0")
+                                       (L"Rmix C 1.0")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"5.1+stereomix", L"2.0", mix_config::add, list_of
+                                       (L"Lmix L 1.0")
+                                       (L"Rmix R 1.0")
+       ));
+       repository.register_mix_config(create_mix_config_from_string(
+                       L"5.1+stereomix", L"5.1", mix_config::add, list_of
+                                       (L"L   L   1.0")
+                                       (L"R   R   1.0")
+                                       (L"C   C   1.0")
+                                       (L"Ls  Ls  1.0")
+                                       (L"Rs  Rs  1.0")
+                                       (L"LFE LFE 1.0")
+       ));
+}
+
+void parse_mix_configs(
+               mix_config_repository& repository, 
+               const boost::property_tree::wptree& channel_mixings_element)
+{
+       BOOST_FOREACH(auto element, channel_mixings_element)
+       {
+               if (element.first != L"mix-config")
+               {
+                       throw boost::property_tree::ptree_error(
+                               "Expected mix-config element");
+               }
+
+               std::vector<std::wstring> mappings;
+
+               boost::transform(
+                               element.second.get_child(L"mappings"),
+                               std::insert_iterator<std::vector<std::wstring>>(
+                                               mappings, mappings.begin()),
+                               [](
+                                               const std::pair<std::wstring,
+                                               boost::property_tree::wptree>& mapping)
+                               {
+                                       return mapping.second.get_value<std::wstring>();
+                               });
+
+               repository.register_mix_config(create_mix_config_from_string(
+                               element.second.get<std::wstring>(L"from"),
+                               element.second.get<std::wstring>(L"to"),
+                               boost::to_upper_copy(element.second.get<std::wstring>(
+                                               L"mix", L"ADD")) == L"AVERAGE"
+                                                               ? mix_config::average : mix_config::add,
+                               mappings));
+       }
+}
+
+mix_config_repository& default_mix_config_repository()
+{
+       static mix_config_repository repository;
+
+       return repository;
+}
+
+channel_layout create_custom_channel_layout(
+               const std::wstring& custom_channel_order,
+               const channel_layout_repository& repository)
+{
+       std::vector<std::wstring> splitted;
+       boost::split(
+                       splitted,
+                       custom_channel_order,
+                       boost::is_any_of(L":"),
+                       boost::token_compress_on);
+
+       if (splitted.size() == 1) // Named layout
+       {
+               try
+               {
+                       return repository.get_by_name(splitted[0]);
+               }
+               catch (const std::exception&)
+               {
+                       CASPAR_LOG_CURRENT_EXCEPTION();
+                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(
+                                       "CHANNEL_LAYOUT must be in a format like: "
+                                       "\"5.1:L R C LFE Ls Rs\" or like \"SMPTE\""));
+               }
+       }
+
+       if (splitted.size() != 2)
+               BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(
+                               "CHANNEL_LAYOUT must be in a format like: "
+                               "\"5.1:L R C LFE Ls Rs\" or like \"SMPTE\""));
+
+       // Custom layout
+       return create_layout_from_string(
+                       custom_channel_order,
+                       splitted[0],
+                       -1,
+                       splitted[1]);
+}
+
+
+}}
index a24f2cbd1d5f8ba30a783c372e219715d8032e3a..4451a419debbd5975099443e7e5ef0e60943f39e 100644 (file)
 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
 *\r
 * Author: Robert Nagy, ronag89@gmail.com\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
 */\r
 \r
 #pragma once\r
 \r
 #include <algorithm>\r
 #include <vector>\r
+#include <map>\r
+#include <string>\r
 \r
 #include <stdint.h>\r
 \r
+#include <boost/range/iterator_range.hpp>\r
+#include <boost/range/algorithm/copy.hpp>\r
+#include <boost/range/combine.hpp>\r
+#include <boost/range/adaptors.hpp>\r
+#include <boost/property_tree/ptree.hpp>\r
+#include <boost/foreach.hpp>\r
+\r
 #include <tbb/cache_aligned_allocator.h>\r
 \r
+#include <common/exception/exceptions.h>\r
+#include <common/utility/iterator.h>\r
+#include <common/utility/string.h>\r
+#include <common/memory/safe_ptr.h>\r
+\r
 namespace caspar { namespace core {\r
        \r
 template<typename T>\r
@@ -62,4 +77,479 @@ static std::vector<int16_t, tbb::cache_aligned_allocator<int16_t>> audio_32_to_1
        return output16;\r
 }\r
 \r
-}}
\ No newline at end of file
+\r
+struct channel_layout\r
+{\r
+       std::wstring name;\r
+       std::wstring layout_type;\r
+       std::vector<std::wstring> channel_names;\r
+       int num_channels;\r
+\r
+       bool operator==(const channel_layout& other) const;\r
+       int channel_index(const std::wstring& channel_name) const;\r
+       bool has_channel(const std::wstring& channel_name) const;\r
+       bool no_channel_names() const;\r
+};\r
+\r
+/**\r
+ * A multichannel view of an audio buffer where the samples has to be\r
+ * interleaved like this (given 4 channels):\r
+ *\r
+ * Memory Sample:   0  1  2  3  4  5  6  7  8  9  10 11\r
+ * Temporal Sample: 0           1           2\r
+ * Channel:         1  2  3  4  1  2  3  4  1  2  3  4\r
+ *\r
+ * Exposes each individual channel as an isolated iterable range.\r
+ */\r
+template<typename SampleT, typename Iter>\r
+class multichannel_view\r
+{\r
+       Iter begin_;\r
+       Iter end_;\r
+       channel_layout channel_layout_;\r
+       int num_channels_;\r
+public:\r
+       typedef position_based_skip_iterator<SampleT, constant_step_finder, Iter>\r
+                       iter_t;\r
+\r
+       multichannel_view(\r
+                       const Iter& begin, \r
+                       const Iter& end, \r
+                       const channel_layout& channel_layout)\r
+               : begin_(begin)\r
+               , end_(end)\r
+               , channel_layout_(channel_layout)\r
+               , num_channels_(channel_layout.num_channels)\r
+       {\r
+       }\r
+\r
+       multichannel_view(\r
+                       const Iter& begin,\r
+                       const Iter& end,\r
+                       const channel_layout& channel_layout,\r
+                       int num_channels)\r
+               : begin_(begin)\r
+               , end_(end)\r
+               , channel_layout_(channel_layout)\r
+               , num_channels_(num_channels)\r
+       {\r
+       }\r
+\r
+       Iter raw_begin() const\r
+       {\r
+               return begin_;\r
+       }\r
+\r
+       Iter raw_end() const\r
+       {\r
+               return end_;\r
+       }\r
+\r
+       int num_channels() const\r
+       {\r
+               return num_channels_;\r
+       }\r
+\r
+       int num_samples() const\r
+       {\r
+               return std::distance(begin_, end_) / num_channels();\r
+       }\r
+\r
+       const channel_layout& channel_layout() const\r
+       {\r
+               return channel_layout_;\r
+       }\r
+\r
+       boost::iterator_range<iter_t> channel(int channel) const\r
+       {\r
+               auto start_position = begin_ + channel;\r
+\r
+               return boost::iterator_range<iter_t>(\r
+                               iter_t(\r
+                                               start_position,\r
+                                               end_,\r
+                                               constant_step_finder(\r
+                                                               num_channels(), num_samples() - 1)),\r
+                               iter_t(end_));\r
+       }\r
+\r
+       boost::iterator_range<iter_t> channel(\r
+                       const std::wstring& channel_name) const\r
+       {\r
+               auto it = std::find(\r
+                               channel_layout_.channel_names.begin(),\r
+                               channel_layout_.channel_names.end(),\r
+                               channel_name);\r
+\r
+               if (it == channel_layout_.channel_names.end())\r
+                       BOOST_THROW_EXCEPTION(invalid_argument() \r
+                                       << msg_info("channel not found " + narrow(channel_name)));\r
+\r
+               auto index = it - channel_layout_.channel_names.begin();\r
+\r
+               return channel(index);\r
+       }\r
+};\r
+\r
+template<typename SampleT, typename Iter>\r
+multichannel_view<SampleT, Iter> make_multichannel_view(\r
+               const Iter& begin,\r
+               const Iter& end,\r
+               const channel_layout& channel_layout)\r
+{\r
+       return multichannel_view<SampleT, Iter>(begin, end, channel_layout);\r
+}\r
+\r
+template<typename SampleT, typename Iter>\r
+multichannel_view<SampleT, Iter> make_multichannel_view(\r
+               const Iter& begin,\r
+               const Iter& end,\r
+               const channel_layout& channel_layout,\r
+               int num_channels)\r
+{\r
+       return multichannel_view<SampleT, Iter>(\r
+                       begin, end, channel_layout, num_channels);\r
+}\r
+\r
+struct mix_config\r
+{\r
+       struct destination\r
+       {\r
+               std::wstring channel_name;\r
+               double influence;\r
+\r
+               destination(const std::wstring& channel_name, double influence)\r
+                       : channel_name(channel_name), influence(influence)\r
+               {\r
+               }\r
+       };\r
+\r
+       enum mix_strategy\r
+       {\r
+               add,\r
+               average\r
+       };\r
+\r
+       std::wstring from_layout_type;\r
+       std::wstring to_layout_type;\r
+       std::multimap<std::wstring, destination> destination_ch_by_source_ch;\r
+       mix_strategy strategy;\r
+};\r
+\r
+bool needs_rearranging(\r
+               const channel_layout& source, const channel_layout& destination);\r
+\r
+template<typename SrcView>\r
+bool needs_rearranging(\r
+               const SrcView& source,\r
+               const channel_layout& destination,\r
+               int destination_num_channels)\r
+{\r
+       return needs_rearranging(source.channel_layout(), destination) \r
+                       || source.num_channels() != destination_num_channels;\r
+}\r
+\r
+template<\r
+       typename SrcSampleT,\r
+       typename DstSampleT,\r
+       typename SrcIter,\r
+       typename DstIter>\r
+void rearrange(\r
+               const multichannel_view<SrcSampleT, SrcIter>& source,\r
+               multichannel_view<DstSampleT, DstIter>& destination)\r
+{\r
+       if (source.channel_layout().no_channel_names()\r
+                       || destination.channel_layout().no_channel_names())\r
+       {\r
+               int num_channels = std::min(\r
+                               source.channel_layout().num_channels,\r
+                               destination.channel_layout().num_channels);\r
+\r
+               for (int i = 0; i < num_channels; ++i)\r
+                       boost::copy(source.channel(i), destination.channel(i).begin());\r
+       }\r
+       else\r
+       {\r
+               BOOST_FOREACH(\r
+                               auto& source_channel_name,\r
+                               source.channel_layout().channel_names)\r
+               {\r
+                       if (source_channel_name.empty()\r
+                                       || !destination.channel_layout().has_channel(\r
+                                                       source_channel_name))\r
+                               continue;\r
+\r
+                       boost::copy(\r
+                                       source.channel(source_channel_name),\r
+                                       destination.channel(source_channel_name).begin());\r
+               }\r
+       }\r
+}\r
+\r
+template<typename SampleT>\r
+struct add\r
+{\r
+       typedef SampleT result_type;\r
+\r
+       result_type operator()(SampleT lhs, SampleT rhs) const\r
+       {\r
+               int64_t result = static_cast<int64_t>(lhs) + static_cast<int64_t>(rhs);\r
+\r
+               if (result > std::numeric_limits<SampleT>::max())\r
+                       return std::numeric_limits<SampleT>::max();\r
+               else if (result < std::numeric_limits<SampleT>::min())\r
+                       return std::numeric_limits<SampleT>::min();\r
+               else\r
+                       return static_cast<SampleT>(result);\r
+       }\r
+};\r
+\r
+template<typename SampleT>\r
+struct attenuate\r
+{\r
+       typedef SampleT result_type;\r
+       double volume;\r
+\r
+       attenuate(double volume)\r
+               : volume(volume)\r
+       {\r
+       }\r
+\r
+       result_type operator()(SampleT sample) const\r
+       {\r
+               return static_cast<SampleT>(sample * volume);\r
+       }\r
+};\r
+\r
+template<typename SampleT>\r
+struct average\r
+{\r
+       typedef SampleT result_type;\r
+       int num_samples_already;\r
+\r
+       average(int num_samples_already)\r
+               : num_samples_already(num_samples_already)\r
+       {\r
+       }\r
+\r
+       result_type operator()(SampleT lhs, SampleT rhs) const\r
+       {\r
+               int64_t total = lhs * num_samples_already + rhs;\r
+\r
+               return static_cast<SampleT>(total / (num_samples_already + 1));\r
+       }\r
+};\r
+\r
+template<typename F>\r
+struct tuple_to_args\r
+{\r
+       typedef typename F::result_type result_type;\r
+       F func;\r
+\r
+       tuple_to_args(F func)\r
+               : func(func)\r
+       {\r
+       }\r
+       \r
+       template<typename T>\r
+       result_type operator()(const T& tuple) const\r
+       {\r
+               return func(boost::get<0>(tuple), boost::get<1>(tuple));\r
+       }\r
+};\r
+\r
+template<typename F>\r
+tuple_to_args<F> make_tuple_to_args(const F& func)\r
+{\r
+       return tuple_to_args<F>(func);\r
+}\r
+\r
+template<\r
+       typename SrcSampleT,\r
+       typename DstSampleT,\r
+       typename SrcIter,\r
+       typename DstIter>\r
+void rearrange_and_mix(\r
+               const multichannel_view<SrcSampleT, SrcIter>& source,\r
+               multichannel_view<DstSampleT, DstIter>& destination,\r
+               const mix_config& config)\r
+{\r
+       using namespace boost;\r
+       using namespace boost::adaptors;\r
+       std::map<std::wstring, int> num_mixed_to_channel;\r
+\r
+       BOOST_FOREACH(auto& elem, config.destination_ch_by_source_ch)\r
+       {\r
+               auto& source_channel_name = elem.first;\r
+               auto& destination_channel_name = elem.second.channel_name;\r
+\r
+               if (!source.channel_layout().has_channel(source_channel_name) ||\r
+                       !destination.channel_layout().has_channel(destination_channel_name))\r
+                       continue;\r
+\r
+               auto source_channel = source.channel(source_channel_name);\r
+               auto destination_channel =\r
+                               destination.channel(destination_channel_name);\r
+               auto influence = elem.second.influence;\r
+\r
+               if (num_mixed_to_channel[destination_channel_name] > 0)\r
+               { // mix\r
+                       if (config.strategy == mix_config::add)\r
+                       {\r
+                               if (influence == 1.0)\r
+                               { // No need to attenuate\r
+                                       copy(\r
+                                               combine(destination_channel, source_channel)\r
+                                                               | transformed(make_tuple_to_args(\r
+                                                                               add<SrcSampleT>())),\r
+                                               destination_channel.begin());\r
+                               }\r
+                               else\r
+                               {\r
+                                       copy(\r
+                                               combine(\r
+                                                       destination_channel,\r
+                                                       source_channel | transformed(\r
+                                                               attenuate<SrcSampleT>(influence)))\r
+                                                                       | transformed(make_tuple_to_args(\r
+                                                                               add<SrcSampleT>())),\r
+                                               destination_channel.begin());\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if (influence == 1.0)\r
+                               { // No need to attenuate\r
+                                       copy(\r
+                                               combine(destination_channel, source_channel)\r
+                                                       | transformed(make_tuple_to_args(\r
+                                                               average<SrcSampleT>(num_mixed_to_channel[\r
+                                                                               destination_channel_name]))),\r
+                                               destination_channel.begin());\r
+                               }\r
+                               else\r
+                               {\r
+                                       copy(\r
+                                               combine(\r
+                                                       destination_channel,\r
+                                                       source_channel | transformed(\r
+                                                               attenuate<SrcSampleT>(influence)))\r
+                                               | transformed(make_tuple_to_args(\r
+                                                       average<SrcSampleT>(num_mixed_to_channel[\r
+                                                                       destination_channel_name]))),\r
+                                               destination_channel.begin());\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               { // copy because the destination only contains zeroes\r
+                       if (influence == 1.0)\r
+                       { // No need to attenuate\r
+                               copy(source_channel, destination_channel.begin());\r
+                       }\r
+                       else\r
+                       {\r
+                               copy(\r
+                                       source_channel | transformed(attenuate<SrcSampleT>(\r
+                                               influence)),\r
+                                       destination_channel.begin());\r
+                       }\r
+               }\r
+\r
+               ++num_mixed_to_channel[destination_channel_name];\r
+       }\r
+}\r
+\r
+class channel_layout_repository\r
+{\r
+public:\r
+       channel_layout_repository();\r
+       ~channel_layout_repository();\r
+       void register_layout(const channel_layout& layout);\r
+       const channel_layout& get_by_name(const std::wstring& layout_name) const;\r
+private:\r
+       struct impl;\r
+       safe_ptr<impl> impl_;\r
+};\r
+\r
+channel_layout create_layout_from_string(\r
+               const std::wstring& name,\r
+               const std::wstring& layout_type,\r
+               int num_channels,\r
+               const std::wstring& channels);\r
+channel_layout create_unspecified_layout(int num_channels);\r
+void register_default_channel_layouts(channel_layout_repository& repository);\r
+void parse_channel_layouts(\r
+               channel_layout_repository& repository,\r
+               const boost::property_tree::wptree& layouts_element);\r
+channel_layout_repository& default_channel_layout_repository();\r
+\r
+class mix_config_repository\r
+{\r
+public:\r
+       mix_config_repository();\r
+       ~mix_config_repository();\r
+       void register_mix_config(const mix_config& config);\r
+       boost::optional<mix_config> get_mix_config(\r
+                       const std::wstring& from_layout_type,\r
+                       const std::wstring& to_layout_type) const;\r
+private:\r
+       struct impl;\r
+       safe_ptr<impl> impl_;\r
+};\r
+\r
+mix_config create_mix_config_from_string(\r
+               const std::wstring& from_layout_type,\r
+               const std::wstring& to_layout_type,\r
+               mix_config::mix_strategy strategy,\r
+               const std::vector<std::wstring>& mappings);\r
+void register_default_mix_configs(mix_config_repository& repository);\r
+void parse_mix_configs(\r
+               mix_config_repository& repository, \r
+               const boost::property_tree::wptree& channel_mixings_element);\r
+mix_config_repository& default_mix_config_repository();\r
+\r
+template<\r
+       typename SrcSampleT,\r
+       typename DstSampleT,\r
+       typename SrcIter,\r
+       typename DstIter>\r
+bool rearrange_or_rearrange_and_mix(\r
+               const multichannel_view<SrcSampleT, SrcIter>& source,\r
+               multichannel_view<DstSampleT, DstIter>& destination,\r
+               const mix_config_repository& repository)\r
+{\r
+       if (source.channel_layout().no_channel_names() \r
+                       || destination.channel_layout().no_channel_names() \r
+                       || source.channel_layout().layout_type\r
+                                       == destination.channel_layout().layout_type)\r
+       {\r
+               rearrange(source, destination);\r
+\r
+               return true;\r
+       }\r
+       else\r
+       {\r
+               auto mix_config = repository.get_mix_config(\r
+                               source.channel_layout().layout_type,\r
+                               destination.channel_layout().layout_type);\r
+\r
+               if (!mix_config)\r
+               {\r
+                       rearrange(source, destination);\r
+\r
+                       return false; // Non-satisfactory mixing, some channels might be\r
+                                     // lost\r
+               }\r
+\r
+               rearrange_and_mix(source, destination, *mix_config);\r
+\r
+               return true;\r
+       }\r
+}\r
+\r
+channel_layout create_custom_channel_layout(\r
+               const std::wstring& custom_channel_order,\r
+               const channel_layout_repository& repository);\r
+\r
+}}\r
index 34bad4560153a560fca9a8d832daa4c172dcc2c6..52974744db2b79eca732fa72f88d1d3a4b2dcd0c 100644 (file)
@@ -35,6 +35,7 @@
 #include <common/gl/gl_check.h>\r
 #include <common/utility/tweener.h>\r
 \r
+#include <core/mixer/audio/audio_util.h>\r
 #include <core/mixer/read_frame.h>\r
 #include <core/mixer/write_frame.h>\r
 #include <core/producer/frame/basic_frame.h>\r
@@ -64,6 +65,7 @@ struct mixer::implementation : boost::noncopyable
        mutable tbb::spin_mutex                 format_desc_mutex_;\r
        video_format_desc                               format_desc_;\r
        safe_ptr<ogl_device>                    ogl_;\r
+       channel_layout                                  audio_channel_layout_;\r
        \r
        audio_mixer     audio_mixer_;\r
        image_mixer image_mixer_;\r
@@ -73,11 +75,12 @@ struct mixer::implementation : boost::noncopyable
        executor executor_;\r
 \r
 public:\r
-       implementation(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<mixer::target_t>& target, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
+       implementation(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<mixer::target_t>& target, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl, const channel_layout& audio_channel_layout\r
                : graph_(graph)\r
                , target_(target)\r
                , format_desc_(format_desc)\r
                , ogl_(ogl)\r
+               , audio_channel_layout_(audio_channel_layout)\r
                , image_mixer_(ogl)\r
                , audio_mixer_(graph_)\r
                , executor_(L"mixer")\r
@@ -107,12 +110,12 @@ public:
                                }\r
 \r
                                auto image = image_mixer_(format_desc_);\r
-                               auto audio = audio_mixer_(format_desc_);\r
+                               auto audio = audio_mixer_(format_desc_, audio_channel_layout_);\r
                                image.wait();\r
 \r
                                graph_->set_value("mix-time", mix_timer_.elapsed()*format_desc_.fps*0.5);\r
 \r
-                               target_->send(std::make_pair(make_safe<read_frame>(ogl_, format_desc_.size, std::move(image.get()), std::move(audio)), packet.second));                                 \r
+                               target_->send(std::make_pair(make_safe<read_frame>(ogl_, format_desc_.size, std::move(image.get()), std::move(audio), audio_channel_layout_), packet.second));\r
                        }\r
                        catch(...)\r
                        {\r
@@ -121,9 +124,12 @@ public:
                });             \r
        }\r
                                        \r
-       safe_ptr<core::write_frame> create_frame(const void* tag, const core::pixel_format_desc& desc)\r
+       safe_ptr<core::write_frame> create_frame(\r
+                       const void* tag,\r
+                       const core::pixel_format_desc& desc,\r
+                       const channel_layout& audio_channel_layout)\r
        {               \r
-               return make_safe<write_frame>(ogl_, tag, desc);\r
+               return make_safe<write_frame>(ogl_, tag, desc, audio_channel_layout);\r
        }\r
                                \r
        void set_blend_mode(int index, blend_mode::type value)\r
@@ -181,11 +187,11 @@ public:
        }\r
 };\r
        \r
-mixer::mixer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<target_t>& target, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
-       : impl_(new implementation(graph, target, format_desc, ogl)){}\r
+mixer::mixer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<target_t>& target, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl, const channel_layout& audio_channel_layout\r
+       : impl_(new implementation(graph, target, format_desc, ogl, audio_channel_layout)){}\r
 void mixer::send(const std::pair<std::map<int, safe_ptr<core::basic_frame>>, std::shared_ptr<void>>& frames){ impl_->send(frames);}\r
 core::video_format_desc mixer::get_video_format_desc() const { return impl_->get_video_format_desc(); }\r
-safe_ptr<core::write_frame> mixer::create_frame(const void* tag, const core::pixel_format_desc& desc){ return impl_->create_frame(tag, desc); }                \r
+safe_ptr<core::write_frame> mixer::create_frame(const void* tag, const core::pixel_format_desc& desc, const channel_layout& audio_channel_layout){ return impl_->create_frame(tag, desc, audio_channel_layout); }              \r
 void mixer::set_blend_mode(int index, blend_mode::type value){impl_->set_blend_mode(index, value);}\r
 void mixer::clear_blend_mode(int index) { impl_->clear_blend_mode(index); }\r
 void mixer::clear_blend_modes() { impl_->clear_blend_modes(); }\r
index 2ee82411c10451ea7ed5a814799a91a257de983f..bd0420d3c8e81d4daa2405eec97a817f767ef96c 100644 (file)
@@ -46,6 +46,7 @@ class basic_frame;
 class ogl_device;\r
 struct frame_transform;\r
 struct pixel_format;\r
+struct channel_layout;\r
 \r
 class mixer : public target<std::pair<std::map<int, safe_ptr<core::basic_frame>>, std::shared_ptr<void>>>\r
                        , public core::frame_factory\r
@@ -53,7 +54,7 @@ class mixer : public target<std::pair<std::map<int, safe_ptr<core::basic_frame>>
 public:        \r
        typedef target<std::pair<safe_ptr<read_frame>, std::shared_ptr<void>>> target_t;\r
 \r
-       explicit mixer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<target_t>& target, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl);\r
+       explicit mixer(const safe_ptr<diagnostics::graph>& graph, const safe_ptr<target_t>& target, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl, const channel_layout& audio_channel_layout);\r
                \r
        // target\r
 \r
@@ -61,7 +62,7 @@ public:
                \r
        // mixer\r
 \r
-       safe_ptr<core::write_frame> create_frame(const void* tag, const core::pixel_format_desc& desc);         \r
+       safe_ptr<core::write_frame> create_frame(const void* tag, const core::pixel_format_desc& desc, const channel_layout& audio_channel_layout);             \r
        \r
        core::video_format_desc get_video_format_desc() const; // nothrow\r
        void set_video_format_desc(const video_format_desc& format_desc);\r
index 9a4b10364d2b532989c46a5dd79ddff17c13dac0..f73bf55687a6de1ce6b8a747d67bbd45152e7085 100644 (file)
@@ -38,13 +38,22 @@ struct read_frame::implementation : boost::noncopyable
        safe_ptr<host_buffer>           image_data_;\r
        tbb::mutex                                      mutex_;\r
        audio_buffer                            audio_data_;\r
+       channel_layout                          audio_channel_layout_;\r
 \r
 public:\r
-       implementation(const safe_ptr<ogl_device>& ogl, size_t size, safe_ptr<host_buffer>&& image_data, audio_buffer&& audio_data) \r
+       implementation(\r
+                       const safe_ptr<ogl_device>& ogl,\r
+                       size_t size,\r
+                       safe_ptr<host_buffer>&& image_data,\r
+                       audio_buffer&& audio_data,\r
+                       const channel_layout& audio_channel_layout) \r
                : ogl_(ogl)\r
                , size_(size)\r
                , image_data_(std::move(image_data))\r
-               , audio_data_(std::move(audio_data)){}  \r
+               , audio_data_(std::move(audio_data))\r
+               , audio_channel_layout_(audio_channel_layout)\r
+       {\r
+       }       \r
        \r
        const boost::iterator_range<const uint8_t*> image_data()\r
        {\r
@@ -67,8 +76,16 @@ public:
        }\r
 };\r
 \r
-read_frame::read_frame(const safe_ptr<ogl_device>& ogl, size_t size, safe_ptr<host_buffer>&& image_data, audio_buffer&& audio_data) \r
-       : impl_(new implementation(ogl, size, std::move(image_data), std::move(audio_data))){}\r
+read_frame::read_frame(\r
+               const safe_ptr<ogl_device>& ogl,\r
+               size_t size,\r
+               safe_ptr<host_buffer>&& image_data,\r
+               audio_buffer&& audio_data,\r
+               const channel_layout& audio_channel_layout) \r
+       : impl_(new implementation(ogl, size, std::move(image_data), std::move(audio_data), audio_channel_layout))\r
+{\r
+}\r
+\r
 read_frame::read_frame(){}\r
 const boost::iterator_range<const uint8_t*> read_frame::image_data()\r
 {\r
@@ -81,6 +98,14 @@ const boost::iterator_range<const int32_t*> read_frame::audio_data()
 }\r
 \r
 size_t read_frame::image_size() const{return impl_ ? impl_->size_ : 0;}\r
+int read_frame::num_channels() const { return impl_ ? impl_->audio_channel_layout_.num_channels : 0; }\r
+const multichannel_view<const int32_t, boost::iterator_range<const int32_t*>::const_iterator> read_frame::multichannel_view() const\r
+{\r
+       return make_multichannel_view<const int32_t>(\r
+                       impl_->audio_data().begin(),\r
+                       impl_->audio_data().end(),\r
+                       impl_->audio_channel_layout_);\r
+}\r
 \r
 //#include <tbb/scalable_allocator.h>\r
 //#include <tbb/parallel_for.h>\r
index 93c344cdf06de22cdec963f2e23df1a25637c023..22759ecc6076da166b1670800d8368aa767d0241 100644 (file)
@@ -24,6 +24,7 @@
 #include <common/memory/safe_ptr.h>\r
 \r
 #include <core/mixer/audio/audio_mixer.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <boost/noncopyable.hpp>\r
 #include <boost/range/iterator_range.hpp>\r
@@ -41,12 +42,19 @@ class read_frame : boost::noncopyable
 {\r
 public:\r
        read_frame();\r
-       read_frame(const safe_ptr<ogl_device>& ogl, size_t size, safe_ptr<host_buffer>&& image_data, audio_buffer&& audio_data);\r
+       read_frame(\r
+                       const safe_ptr<ogl_device>& ogl,\r
+                       size_t size,\r
+                       safe_ptr<host_buffer>&& image_data,\r
+                       audio_buffer&& audio_data,\r
+                       const channel_layout& audio_channel_layout);\r
 \r
        virtual const boost::iterator_range<const uint8_t*> image_data();\r
        virtual const boost::iterator_range<const int32_t*> audio_data();\r
 \r
        virtual size_t image_size() const;\r
+       virtual int num_channels() const;\r
+       virtual const multichannel_view<const int32_t, boost::iterator_range<const int32_t*>::const_iterator> multichannel_view() const;\r
                \r
 private:\r
        struct implementation;\r
index 93889add103be9edf32c54ecfe1b7db91f01a8e0..570990c9ea83e222c959a4545f65bd9d7c7a16e9 100644 (file)
@@ -29,6 +29,7 @@
 \r
 #include <core/producer/frame/frame_visitor.h>\r
 #include <core/producer/frame/pixel_format.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <boost/lexical_cast.hpp>\r
 \r
@@ -41,17 +42,20 @@ struct write_frame::implementation
        std::vector<safe_ptr<device_buffer>>            textures_;\r
        audio_buffer                                                            audio_data_;\r
        const core::pixel_format_desc                           desc_;\r
+       const channel_layout                                            channel_layout_;\r
        const void*                                                                     tag_;\r
        core::field_mode::type                                          mode_;\r
 \r
-       implementation(const void* tag)\r
-               : tag_(tag)\r
+       implementation(const void* tag, const channel_layout& channel_layout)\r
+               : channel_layout_(channel_layout)\r
+               , tag_(tag)\r
        {\r
        }\r
 \r
-       implementation(const safe_ptr<ogl_device>& ogl, const void* tag, const core::pixel_format_desc& desc) \r
+       implementation(const safe_ptr<ogl_device>& ogl, const void* tag, const core::pixel_format_desc& desc, const channel_layout& channel_layout\r
                : ogl_(ogl)\r
                , desc_(desc)\r
+               , channel_layout_(channel_layout)\r
                , tag_(tag)\r
                , mode_(core::field_mode::progressive)\r
        {\r
@@ -108,9 +112,18 @@ struct write_frame::implementation
        }\r
 };\r
        \r
-write_frame::write_frame(const void* tag) : impl_(new implementation(tag)){}\r
-write_frame::write_frame(const safe_ptr<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 void* tag, const channel_layout& channel_layout)\r
+       : impl_(new implementation(tag, channel_layout))\r
+{\r
+}\r
+write_frame::write_frame(\r
+               const safe_ptr<ogl_device>& ogl,\r
+               const void* tag,\r
+               const core::pixel_format_desc& desc,\r
+               const channel_layout& channel_layout)\r
+       : impl_(new implementation(ogl, tag, desc, channel_layout))\r
+{\r
+}\r
 write_frame::write_frame(const write_frame& other) : impl_(new implementation(*other.impl_)){}\r
 write_frame::write_frame(write_frame&& other) : impl_(std::move(other.impl_)){}\r
 write_frame& write_frame::operator=(const write_frame& other)\r
@@ -131,6 +144,11 @@ boost::iterator_range<uint8_t*> write_frame::image_data(size_t index){return imp
 audio_buffer& write_frame::audio_data() { return impl_->audio_data_; }\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 channel_layout& write_frame::get_channel_layout() const{return impl_->channel_layout_;}\r
+multichannel_view<int32_t, audio_buffer::iterator> write_frame::get_multichannel_view()\r
+{\r
+       return make_multichannel_view<int32_t>(impl_->audio_data_.begin(), impl_->audio_data_.end(), impl_->channel_layout_);\r
+}\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
 void write_frame::commit(){impl_->commit();}\r
index 7c75d021daeff90f9272e3ac6297eb496eb8b9e8..08fbb6cd3cf964a098aac7358f554404af1089ee 100644 (file)
@@ -26,6 +26,7 @@
 #include <core/producer/frame/basic_frame.h>\r
 #include <core/video_format.h>\r
 #include <core/mixer/audio/audio_mixer.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <boost/noncopyable.hpp>\r
 #include <boost/range/iterator_range.hpp>\r
@@ -43,8 +44,8 @@ class ogl_device;
 class write_frame : public core::basic_frame, boost::noncopyable\r
 {\r
 public:        \r
-       explicit write_frame(const void* tag);\r
-       explicit write_frame(const safe_ptr<ogl_device>& ogl, const void* tag, const core::pixel_format_desc& desc);\r
+       explicit write_frame(const void* tag, const channel_layout& channel_layout);\r
+       explicit write_frame(const safe_ptr<ogl_device>& ogl, const void* tag, const core::pixel_format_desc& desc, const channel_layout& channel_layout);\r
 \r
        write_frame(const write_frame& other);\r
        write_frame(write_frame&& other);\r
@@ -72,6 +73,8 @@ public:
        const void* tag() const;\r
 \r
        const core::pixel_format_desc& get_pixel_format_desc() const;\r
+       const channel_layout& get_channel_layout() const;\r
+       multichannel_view<int32_t, audio_buffer::iterator> get_multichannel_view();\r
        \r
 private:\r
        friend class image_mixer;\r
index 7656feeac29c08a74a7cc34671cb282d1b099602..89285b671ceefd47b3c57003adcb22e51df21f04 100644 (file)
@@ -24,6 +24,7 @@
 #include "pixel_format.h"\r
 \r
 #include <common/memory/safe_ptr.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <boost/noncopyable.hpp>\r
 \r
@@ -36,8 +37,11 @@ struct video_format_desc;
 struct frame_factory : boost::noncopyable\r
 {\r
        virtual ~frame_factory(){}\r
-       virtual safe_ptr<write_frame> create_frame(const void* video_stream_tag, const pixel_format_desc& desc) = 0;    \r
-       \r
+       virtual safe_ptr<write_frame> create_frame(\r
+                       const void* video_stream_tag,\r
+                       const pixel_format_desc& desc,\r
+                       const channel_layout& audio_channel_layout = default_channel_layout_repository().get_by_name(L"STEREO")) = 0;   \r
+\r
        virtual video_format_desc get_video_format_desc() const = 0; // nothrow\r
 };\r
 \r
index d98495fd2586b376dfc10d31d2fc8b40fd0974d1..a4099cecff317adaa463f25cf01bc2a6bae57603 100644 (file)
@@ -36,6 +36,7 @@
 #include "producer/frame_producer.h"\r
 #include "consumer/frame_consumer.h"\r
 #include "mixer/mixer.h"\r
+#include "mixer/audio/audio_util.h"\r
 #include "video_format.h"\r
 #include "producer/frame/basic_frame.h"\r
 #include "producer/frame/frame_transform.h"\r
@@ -120,7 +121,12 @@ public:
                , ogl_(ogl)\r
                , format_desc_(render_video_mode)\r
                , output_(new thumbnail_output(generate_delay_millis))\r
-               , mixer_(new mixer(graph_, output_, format_desc_, ogl))\r
+               , mixer_(new mixer(\r
+                               graph_,\r
+                               output_,\r
+                               format_desc_,\r
+                               ogl,\r
+                               default_channel_layout_repository().get_by_name(L"STEREO")))\r
                , thumbnail_creator_(thumbnail_creator)\r
                , monitor_(monitor_factory.create(\r
                                media_path,\r
index d8f864ee4aa3d30152ef3652402fcf07f1757163..059e8d631e96044e195328ef03e59c565fa8ac47 100644 (file)
@@ -28,6 +28,7 @@
 #include "consumer/output.h"\r
 #include "mixer/mixer.h"\r
 #include "mixer/gpu/ogl_device.h"\r
+#include "mixer/audio/audio_util.h"\r
 #include "producer/stage.h"\r
 \r
 #include <common/diagnostics/graph.h>\r
@@ -54,13 +55,13 @@ struct video_channel::implementation : boost::noncopyable
        monitor::subject                                                monitor_subject_;\r
        \r
 public:\r
-       implementation(video_channel& self, int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl)  \r
+       implementation(video_channel& self, int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl, const channel_layout& audio_channel_layout)  \r
                : self_(self)\r
                , index_(index)\r
                , format_desc_(format_desc)\r
                , ogl_(ogl)\r
                , output_(new caspar::core::output(graph_, format_desc, index))\r
-               , mixer_(new caspar::core::mixer(graph_, output_, format_desc, ogl))\r
+               , mixer_(new caspar::core::mixer(graph_, output_, format_desc, ogl, audio_channel_layout))\r
                , stage_(new caspar::core::stage(graph_, mixer_, format_desc))  \r
                , monitor_subject_("/channel/" + boost::lexical_cast<std::string>(index))\r
        {\r
@@ -123,8 +124,8 @@ public:
        }\r
 };\r
 \r
-video_channel::video_channel(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl) \r
-       : impl_(new implementation(*this, index, format_desc, ogl)){}\r
+video_channel::video_channel(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl, const channel_layout& audio_channel_layout\r
+       : impl_(new implementation(*this, index, format_desc, ogl, audio_channel_layout)){}\r
 safe_ptr<stage> video_channel::stage() { return impl_->stage_;} \r
 safe_ptr<mixer> video_channel::mixer() { return impl_->mixer_;} \r
 safe_ptr<output> video_channel::output() { return impl_->output_;} \r
index 89312db1dc8cec638b6d80665cab5d554aa0f8cd..ee470eedd660f7bc1fda41dd83f55d89fcedce23 100644 (file)
@@ -37,6 +37,7 @@ class mixer;
 class output;\r
 class ogl_device;\r
 struct video_format_desc;\r
+struct channel_layout;\r
 \r
 class video_channel : boost::noncopyable\r
 {\r
@@ -46,7 +47,7 @@ public:
 \r
        // Constructors\r
 \r
-       explicit video_channel(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl);\r
+       explicit video_channel(int index, const video_format_desc& format_desc, const safe_ptr<ogl_device>& ogl, const channel_layout& audio_channel_layout);\r
 \r
        // Methods\r
 \r
index da2173be201df4c54a13132b9ede124dfb12cb30..a5bc10f52614200c50f7ba3910cd14553ddec913 100644 (file)
@@ -41,7 +41,6 @@
        ((w)*(h)*4),\\r
        (name),\\r
        (48000),\\r
-       (2),\\r
        (audio_samples)\\r
 }\r
 \r
index 77bfb71c3e13972a5a5f6890ce0b0ed1ce6ff93f..4b50e075db88c77681a2e4f7530530478a0ce55a 100644 (file)
@@ -108,7 +108,6 @@ struct video_format_desc
        std::wstring                    name;           // name of output format\r
 \r
        size_t                                  audio_sample_rate;\r
-       size_t                                  audio_channels;\r
        std::vector<size_t>             audio_cadence; // rotating optimal number of samples per frame\r
 \r
        static const video_format_desc& get(video_format::type format);\r
index 0ef03eea40b67d44cb1f887ac83b619f53d9c251..4f1740e6d5e057b8348e6c93031f4179d8ae4783 100644 (file)
@@ -34,6 +34,7 @@
 #include <common/memory/memcpy.h>\r
 #include <common/memory/memshfl.h>\r
 #include <common/utility/timer.h>\r
+#include <common/utility/param.h>\r
 \r
 #include <core/consumer/frame_consumer.h>\r
 #include <core/mixer/audio/audio_util.h>\r
@@ -42,6 +43,7 @@
 \r
 #include <boost/timer.hpp>\r
 #include <boost/range/algorithm.hpp>\r
+#include <boost/algorithm/string.hpp>\r
 #include <boost/property_tree/ptree.hpp>\r
 \r
 #include <memory>\r
@@ -55,6 +57,7 @@ struct bluefish_consumer : boost::noncopyable
        const unsigned int                                      device_index_;\r
        const core::video_format_desc           format_desc_;\r
        const int                                                       channel_index_;\r
+       const core::channel_layout                      channel_layout_;\r
 \r
        const std::wstring                                      model_name_;\r
 \r
@@ -73,11 +76,18 @@ struct bluefish_consumer : boost::noncopyable
                \r
        executor                                                        executor_;\r
 public:\r
-       bluefish_consumer(const core::video_format_desc& format_desc, unsigned int device_index, bool embedded_audio, bool key_only, int channel_index) \r
+       bluefish_consumer(\r
+                       const core::video_format_desc& format_desc,\r
+                       unsigned int device_index,\r
+                       bool embedded_audio,\r
+                       bool key_only,\r
+                       int channel_index,\r
+                       const core::channel_layout& channel_layout)\r
                : blue_(create_blue(device_index))\r
                , device_index_(device_index)\r
                , format_desc_(format_desc) \r
                , channel_index_(channel_index)\r
+               , channel_layout_(channel_layout)\r
                , model_name_(get_card_desc(*blue_))\r
                , vid_fmt_(get_video_mode(*blue_, format_desc))\r
                , embedded_audio_(embedded_audio)\r
@@ -132,7 +142,19 @@ public:
                }\r
                else\r
                {\r
-                       if(BLUE_FAIL(set_card_property(blue_, EMBEDEDDED_AUDIO_OUTPUT, blue_emb_audio_enable | blue_emb_audio_group1_enable))) \r
+                       ULONG audio_value =\r
+                               EMBEDDED_AUDIO_OUTPUT | blue_emb_audio_group1_enable;\r
+\r
+                       if (channel_layout_.num_channels > 4)\r
+                               audio_value |= blue_emb_audio_group2_enable;\r
+\r
+                       if (channel_layout_.num_channels > 8)\r
+                               audio_value |= blue_emb_audio_group3_enable;\r
+\r
+                       if (channel_layout_.num_channels > 12)\r
+                               audio_value |= blue_emb_audio_group4_enable;\r
+\r
+                       if(BLUE_FAIL(set_card_property(blue_, EMBEDEDDED_AUDIO_OUTPUT, audio_value))) \r
                                CASPAR_LOG(warning) << print() << TEXT(" Failed to enable embedded audio.");                    \r
                        CASPAR_LOG(info) << print() << TEXT(" Enabled embedded-audio.");\r
                }\r
@@ -230,9 +252,40 @@ public:
                // Send and display\r
 \r
                if(embedded_audio_)\r
-               {               \r
-                       auto frame_audio = core::audio_32_to_24(frame->audio_data());                   \r
-                       encode_hanc(reinterpret_cast<BLUE_UINT32*>(reserved_frames_.front()->hanc_data()), frame_audio.data(), frame->audio_data().size()/format_desc_.audio_channels, format_desc_.audio_channels);\r
+               {\r
+                       auto src_view = frame->multichannel_view();\r
+\r
+                       if (core::needs_rearranging(src_view, channel_layout_, channel_layout_.num_channels))\r
+                       {\r
+                               std::vector<int32_t> resulting_audio_data;\r
+                               resulting_audio_data.resize(src_view.num_samples() * channel_layout_.num_channels, 0);\r
+\r
+                               auto dest_view = core::make_multichannel_view<int32_t>(\r
+                                               resulting_audio_data.begin(), \r
+                                               resulting_audio_data.end(),\r
+                                               channel_layout_);\r
+\r
+                               core::rearrange_or_rearrange_and_mix(\r
+                                               src_view,\r
+                                               dest_view,\r
+                                               core::default_mix_config_repository());\r
+\r
+                               auto frame_audio = core::audio_32_to_24(resulting_audio_data);\r
+                               encode_hanc(\r
+                                               reinterpret_cast<BLUE_UINT32*>(reserved_frames_.front()->hanc_data()),\r
+                                               frame_audio.data(),\r
+                                               src_view.num_samples(),\r
+                                               channel_layout_.num_channels);\r
+                       }\r
+                       else\r
+                       {\r
+                               auto frame_audio = core::audio_32_to_24(frame->audio_data());\r
+                               encode_hanc(\r
+                                               reinterpret_cast<BLUE_UINT32*>(reserved_frames_.front()->hanc_data()),\r
+                                               frame_audio.data(),\r
+                                               src_view.num_samples(),\r
+                                               channel_layout_.num_channels);\r
+                       }\r
                                                                \r
                        blue_->system_buffer_write_async(const_cast<uint8_t*>(reserved_frames_.front()->image_data()), \r
                                                                                        reserved_frames_.front()->image_size(), \r
@@ -266,7 +319,16 @@ public:
        void encode_hanc(BLUE_UINT32* hanc_data, void* audio_data, size_t audio_samples, size_t audio_nchannels)\r
        {       \r
                const auto sample_type = AUDIO_CHANNEL_24BIT | AUDIO_CHANNEL_LITTLEENDIAN;\r
-               const auto emb_audio_flag = blue_emb_audio_enable | blue_emb_audio_group1_enable;\r
+               auto emb_audio_flag = blue_emb_audio_enable | blue_emb_audio_group1_enable;\r
+\r
+               if (audio_nchannels > 4)\r
+                       emb_audio_flag |= blue_emb_audio_group2_enable;\r
+\r
+               if (audio_nchannels > 8)\r
+                       emb_audio_flag |= blue_emb_audio_group3_enable;\r
+\r
+               if (audio_nchannels > 12)\r
+                       emb_audio_flag |= blue_emb_audio_group4_enable;\r
                \r
                hanc_stream_info_struct hanc_stream_info;\r
                memset(&hanc_stream_info, 0, sizeof(hanc_stream_info));\r
@@ -299,12 +361,19 @@ struct bluefish_consumer_proxy : public core::frame_consumer
        const bool                                                      key_only_;\r
        std::vector<size_t>                                     audio_cadence_;\r
        core::video_format_desc                         format_desc_;\r
+       core::channel_layout                            channel_layout_;\r
+\r
 public:\r
 \r
-       bluefish_consumer_proxy(size_t device_index, bool embedded_audio, bool key_only)\r
+       bluefish_consumer_proxy(\r
+                       size_t device_index,\r
+                       bool embedded_audio,\r
+                       bool key_only,\r
+                       const core::channel_layout& channel_layout)\r
                : device_index_(device_index)\r
                , embedded_audio_(embedded_audio)\r
                , key_only_(key_only)\r
+               , channel_layout_(channel_layout)\r
        {\r
        }\r
        \r
@@ -322,7 +391,13 @@ public:
        \r
        virtual void initialize(const core::video_format_desc& format_desc, int channel_index) override\r
        {\r
-               consumer_.reset(new bluefish_consumer(format_desc, device_index_, embedded_audio_, key_only_, channel_index));\r
+               consumer_.reset(new bluefish_consumer(\r
+                               format_desc,\r
+                               device_index_,\r
+                               embedded_audio_,\r
+                               key_only_,\r
+                               channel_index,\r
+                               channel_layout_));\r
                audio_cadence_ = format_desc.audio_cadence;\r
                format_desc_ = format_desc;\r
                CASPAR_LOG(info) << print() << L" Successfully Initialized.";   \r
@@ -330,7 +405,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 consumer_->send(frame);\r
@@ -369,10 +444,12 @@ safe_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>&
                \r
        const auto device_index = params.size() > 1 ? lexical_cast_or_default<int>(params[1], 1) : 1;\r
 \r
-       const auto embedded_audio = std::find(params.begin(), params.end(), L"EMBEDDED_AUDIO") != params.end();\r
-       const auto key_only               = std::find(params.begin(), params.end(), L"KEY_ONLY")           != params.end();\r
+       const auto embedded_audio       = std::find(params.begin(), params.end(), L"EMBEDDED_AUDIO") != params.end();\r
+       const auto key_only                     = std::find(params.begin(), params.end(), L"KEY_ONLY")     != params.end();\r
+       const auto audio_layout         = core::default_channel_layout_repository().get_by_name(\r
+                       get_param(L"CHANNEL_LAYOUT", params, L"STEREO"));\r
 \r
-       return make_safe<bluefish_consumer_proxy>(device_index, embedded_audio, key_only);\r
+       return make_safe<bluefish_consumer_proxy>(device_index, embedded_audio, key_only, audio_layout);\r
 }\r
 \r
 safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptree& ptree) \r
@@ -380,8 +457,12 @@ safe_ptr<core::frame_consumer> create_consumer(const boost::property_tree::wptre
        const auto device_index         = ptree.get(L"device",                  1);\r
        const auto embedded_audio       = ptree.get(L"embedded-audio",  false);\r
        const auto key_only                     = ptree.get(L"key-only",                false);\r
+       const auto 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<bluefish_consumer_proxy>(device_index, embedded_audio, key_only);\r
+       return make_safe<bluefish_consumer_proxy>(\r
+                       device_index, embedded_audio, key_only, audio_layout);\r
 }\r
 \r
 }}
\ No newline at end of file
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
index 18d962938466347f3f9232353dcc9e2078093c8e..4c6d3b9685dad6c532a9f40bf19bc865acc201a8 100644 (file)
     <ClCompile Include="interop\DeckLinkAPI_i.c">\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">NotUsing</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">NotUsing</PrecompiledHeader>\r
     </ClCompile>\r
     <ClCompile Include="producer\decklink_producer.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
index 9ba419f8ff1ac7a592c878e81b4110284d9c0814..164185ac2838c01988a3f91fff818b64553eec69 100644 (file)
@@ -40,6 +40,7 @@
 \r
 #include <core/monitor/monitor.h>\r
 #include <core/mixer/write_frame.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 #include <core/producer/frame/frame_transform.h>\r
 #include <core/producer/frame/frame_factory.h>\r
 \r
@@ -103,10 +104,18 @@ class decklink_producer : boost::noncopyable, public IDeckLinkInputCallback
 \r
        tbb::concurrent_bounded_queue<safe_ptr<core::basic_frame>>      frame_buffer_;\r
 \r
-       std::exception_ptr                                                                                      exception_;             \r
+       std::exception_ptr                                                                                      exception_;     \r
+       int                                                                                                                     num_input_channels_;\r
+       core::channel_layout                                                                            audio_channel_layout_;\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, std::size_t buffer_depth)\r
+       decklink_producer(\r
+                       const core::video_format_desc& format_desc,\r
+                       const core::channel_layout& audio_channel_layout,\r
+                       size_t device_index,\r
+                       const safe_ptr<core::frame_factory>& frame_factory,\r
+                       const std::wstring& filter,\r
+                       std::size_t buffer_depth)\r
                : decklink_(get_device(device_index))\r
                , input_(decklink_)\r
                , attributes_(decklink_)\r
@@ -115,12 +124,20 @@ public:
                , filter_(filter)\r
                , format_desc_(format_desc)\r
                , audio_cadence_(format_desc.audio_cadence)\r
-               , muxer_(format_desc.fps, frame_factory, false, filter)\r
+               , muxer_(format_desc.fps, frame_factory, false, audio_channel_layout, filter)\r
                , sync_buffer_(format_desc.audio_cadence.size())\r
                , frame_factory_(frame_factory)\r
+               , audio_channel_layout_(audio_channel_layout)\r
        {               \r
                hints_ = 0;\r
                frame_buffer_.set_capacity(buffer_depth);\r
+\r
+               if (audio_channel_layout.num_channels <= 2)\r
+                       num_input_channels_ = 2;\r
+               else if (audio_channel_layout.num_channels <= 8)\r
+                       num_input_channels_ = 8;\r
+               else\r
+                       num_input_channels_ = 16;\r
                \r
                graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   \r
                graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));\r
@@ -138,7 +155,7 @@ public:
                                                                        << msg_info(narrow(print()) + " Could not enable video input.")\r
                                                                        << boost::errinfo_api_function("EnableVideoInput"));\r
 \r
-               if(FAILED(input_->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, format_desc_.audio_channels))) \r
+               if(FAILED(input_->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, num_input_channels_))) \r
                        BOOST_THROW_EXCEPTION(caspar_exception() \r
                                                                        << msg_info(narrow(print()) + " Could not enable audio input.")\r
                                                                        << boost::errinfo_api_function("EnableAudioInput"));\r
@@ -208,15 +225,37 @@ public:
                        {\r
                                auto sample_frame_count = audio->GetSampleFrameCount();\r
                                auto audio_data = reinterpret_cast<int32_t*>(bytes);\r
-                               audio_buffer = std::make_shared<core::audio_buffer>(audio_data, audio_data + sample_frame_count*format_desc_.audio_channels);\r
+\r
+                               if (num_input_channels_ == audio_channel_layout_.num_channels)\r
+                               {\r
+                                       audio_buffer = std::make_shared<core::audio_buffer>(\r
+                                                       audio_data, \r
+                                                       audio_data + sample_frame_count * num_input_channels_);\r
+                               }\r
+                               else\r
+                               {\r
+                                       audio_buffer = std::make_shared<core::audio_buffer>();\r
+                                       audio_buffer->resize(sample_frame_count * audio_channel_layout_.num_channels, 0);\r
+                                       auto src_view = core::make_multichannel_view<int32_t>(\r
+                                                       audio_data, \r
+                                                       audio_data + sample_frame_count * num_input_channels_, \r
+                                                       audio_channel_layout_, \r
+                                                       num_input_channels_);\r
+                                       auto dst_view = core::make_multichannel_view<int32_t>(\r
+                                                       audio_buffer->begin(),\r
+                                                       audio_buffer->end(),\r
+                                                       audio_channel_layout_);\r
+\r
+                                       core::rearrange(src_view, dst_view);\r
+                               }\r
                        }\r
                        else                    \r
-                               audio_buffer = std::make_shared<core::audio_buffer>(audio_cadence_.front() * format_desc_.audio_channels, 0);\r
+                               audio_buffer = std::make_shared<core::audio_buffer>(audio_cadence_.front() * audio_channel_layout_.num_channels, 0);\r
                        \r
                        // Note: Uses 1 step rotated cadence for 1001 modes (1602, 1602, 1601, 1602, 1601)\r
                        // This cadence fills the audio mixer most optimally.\r
 \r
-                       sync_buffer_.push_back(audio_buffer->size() / format_desc_.audio_channels);             \r
+                       sync_buffer_.push_back(audio_buffer->size() / audio_channel_layout_.num_channels);              \r
                        if(!boost::range::equal(sync_buffer_, audio_cadence_))\r
                        {\r
                                CASPAR_LOG(trace) << print() << L" Syncing audio.";\r
@@ -288,17 +327,25 @@ class decklink_producer_proxy : public core::frame_producer
        const uint32_t                                  length_;\r
 public:\r
 \r
-       explicit decklink_producer_proxy(const safe_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, size_t device_index, const std::wstring& filter_str, uint32_t length, std::size_t buffer_depth)\r
+       explicit decklink_producer_proxy(\r
+                       const safe_ptr<core::frame_factory>& frame_factory,\r
+                       const core::video_format_desc& format_desc,\r
+                       const core::channel_layout& audio_channel_layout,\r
+                       size_t device_index,\r
+                       const std::wstring& filter_str,\r
+                       uint32_t length,\r
+                       std::size_t buffer_depth)\r
                : context_(L"decklink_producer[" + boost::lexical_cast<std::wstring>(device_index) + L"]")\r
                , last_frame_(core::basic_frame::empty())\r
                , length_(length)\r
        {\r
-               context_.reset([&]{return new decklink_producer(format_desc, device_index, frame_factory, filter_str, buffer_depth);}); \r
+               context_.reset([&]{return new decklink_producer(format_desc, audio_channel_layout, device_index, frame_factory, filter_str, buffer_depth);}); \r
        }\r
        \r
        // frame_producer\r
                                \r
-       virtual safe_ptr<core::basic_frame> receive(int hints) override\r
+       virtual safe_ptr<core::basic_frame> receive(\r
+                       int hints) override\r
        {\r
                auto frame = context_->get_frame(hints);\r
                if(frame != core::basic_frame::late())\r
@@ -345,10 +392,13 @@ safe_ptr<core::frame_producer> create_producer(
        auto device_index       = get_param(L"DEVICE", params, -1);\r
        if(device_index == -1)\r
                device_index = boost::lexical_cast<int>(params.at(1));\r
-       auto filter_str         = get_param(L"FILTER", params);         \r
-       auto length                     = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());   \r
-       auto buffer_depth       = get_param(L"BUFFER", params, 2);      \r
-       auto format_desc        = core::video_format_desc::get(get_param(L"FORMAT", params, L"INVALID"));\r
+       auto filter_str                 = get_param(L"FILTER", params);         \r
+       auto length                             = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());   \r
+       auto buffer_depth               = get_param(L"BUFFER", params, 2);      \r
+       auto format_desc                = core::video_format_desc::get(get_param(L"FORMAT", params, L"INVALID"));\r
+       auto audio_layout               = core::create_custom_channel_layout(\r
+                       get_param(L"CHANNEL_LAYOUT", params, L"STEREO"),\r
+                       core::default_channel_layout_repository());\r
        \r
        boost::replace_all(filter_str, L"DEINTERLACE", L"YADIF=0:-1");\r
        boost::replace_all(filter_str, L"DEINTERLACE_BOB", L"YADIF=1:-1");\r
@@ -358,7 +408,7 @@ safe_ptr<core::frame_producer> create_producer(
                        \r
        return create_producer_print_proxy(\r
                   create_producer_destroy_proxy(\r
-                       make_safe<decklink_producer_proxy>(frame_factory, format_desc, device_index, filter_str, length, buffer_depth)));\r
+                       make_safe<decklink_producer_proxy>(frame_factory, format_desc, audio_layout, device_index, filter_str, length, buffer_depth)));\r
 }\r
 \r
 }}
\ No newline at end of file
index 7fd57edf41188080debc34a4c6487b20072009c6..ab72ad8802e43ef80a5e262bf0be94d5828ed48c 100644 (file)
@@ -223,6 +223,7 @@ struct ffmpeg_consumer : boost::noncopyable
                \r
        const std::shared_ptr<AVFormatContext>  oc_;\r
        const core::video_format_desc                   format_desc_;\r
+       const core::channel_layout                              channel_layout_;\r
        \r
        const safe_ptr<diagnostics::graph>              graph_;\r
 \r
@@ -246,12 +247,13 @@ struct ffmpeg_consumer : boost::noncopyable
        bool                                                                    key_only_;\r
        \r
 public:\r
-       ffmpeg_consumer(const std::string& filename, const core::video_format_desc& format_desc, std::vector<option> options, bool key_only)\r
+       ffmpeg_consumer(const std::string& filename, const core::video_format_desc& format_desc, std::vector<option> options, bool key_only, const core::channel_layout& audio_channel_layout)\r
                : filename_(filename)\r
                , video_outbuf_(1920*1080*8)\r
                , audio_outbuf_(10000)\r
                , oc_(avformat_alloc_context(), av_free)\r
                , format_desc_(format_desc)\r
+               , channel_layout_(audio_channel_layout)\r
                , encode_executor_(print())\r
                , in_frame_number_(0)\r
                , out_frame_number_(0)\r
@@ -441,7 +443,7 @@ public:
                c->codec_id                     = output_format_.acodec;\r
                c->codec_type           = AVMEDIA_TYPE_AUDIO;\r
                c->sample_rate          = 48000;\r
-               c->channels                     = 2;\r
+               c->channels                     = channel_layout_.num_channels;\r
                c->sample_fmt           = SAMPLE_FMT_S16;\r
 \r
                if(output_format_.vcodec == CODEC_ID_FLV1)              \r
@@ -543,7 +545,7 @@ public:
        byte_vector convert_audio(core::read_frame& frame, AVCodecContext* c)\r
        {\r
                if(!swr_)               \r
-                       swr_.reset(new audio_resampler(c->channels, format_desc_.audio_channels\r
+                       swr_.reset(new audio_resampler(c->channels, frame.num_channels()\r
                                                                                   c->sample_rate, format_desc_.audio_sample_rate,\r
                                                                                   c->sample_fmt, AV_SAMPLE_FMT_S32));\r
                \r
@@ -639,6 +641,7 @@ struct ffmpeg_consumer_proxy : public core::frame_consumer
        const std::wstring                              filename_;\r
        const std::vector<option>               options_;\r
        const bool                                              separate_key_;\r
+       core::video_format_desc                 format_desc_;\r
 \r
        std::unique_ptr<ffmpeg_consumer> consumer_;\r
        std::unique_ptr<ffmpeg_consumer> key_only_consumer_;\r
@@ -654,22 +657,14 @@ public:
        \r
        virtual void initialize(const core::video_format_desc& format_desc, int)\r
        {\r
-               consumer_.reset();\r
-               key_only_consumer_.reset();\r
-               consumer_.reset(new ffmpeg_consumer(narrow(filename_), format_desc, options_, false));\r
-\r
-               if (separate_key_)\r
-               {\r
-                       boost::filesystem::wpath fill_file(filename_);\r
-                       auto without_extension = fill_file.stem();\r
-                       auto key_file = env::media_folder() + without_extension + L"_A" + fill_file.extension();\r
-                       \r
-                       key_only_consumer_.reset(new ffmpeg_consumer(narrow(key_file), format_desc, options_, true));\r
-               }\r
+               format_desc_ = format_desc;\r
        }\r
        \r
        virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override\r
        {\r
+               if (!consumer_)\r
+                       do_initialize(frame->multichannel_view().channel_layout());\r
+\r
                bool ready_for_frame = consumer_->ready_for_frame();\r
 \r
                if (ready_for_frame && separate_key_)\r
@@ -721,6 +716,32 @@ public:
        {\r
                return 200;\r
        }\r
+private:\r
+       void do_initialize(const core::channel_layout& channel_layout)\r
+       {\r
+               consumer_.reset();\r
+               key_only_consumer_.reset();\r
+               consumer_.reset(new ffmpeg_consumer(\r
+                               narrow(filename_),\r
+                               format_desc_,\r
+                               options_,\r
+                               false,\r
+                               channel_layout));\r
+\r
+               if (separate_key_)\r
+               {\r
+                       boost::filesystem::wpath fill_file(filename_);\r
+                       auto without_extension = fill_file.stem();\r
+                       auto key_file = env::media_folder() + without_extension + L"_A" + fill_file.extension();\r
+                       \r
+                       key_only_consumer_.reset(new ffmpeg_consumer(\r
+                                       narrow(key_file),\r
+                                       format_desc_,\r
+                                       options_,\r
+                                       true,\r
+                                       channel_layout));\r
+               }\r
+       }\r
 };     \r
 \r
 safe_ptr<core::frame_consumer> create_consumer(const std::vector<std::wstring>& params)\r
index b032c0ff1a53071c818349d246359f91a7fea864..c2a7e2a08f72dcd8d4269db82684a479b3f71c0b 100644 (file)
@@ -29,6 +29,7 @@
 #include "../../ffmpeg_error.h"\r
 \r
 #include <core/video_format.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <tbb/cache_aligned_allocator.h>\r
 \r
@@ -63,17 +64,22 @@ struct audio_decoder::implementation : boost::noncopyable
 \r
        const int64_t                                                                                           nb_frames_;\r
        tbb::atomic<size_t>                                                                                     file_frame_number_;\r
+       core::channel_layout                                                                            channel_layout_;\r
 public:\r
-       explicit implementation(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc) \r
+       explicit implementation(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc, const std::wstring& custom_channel_order\r
                : format_desc_(format_desc)     \r
                , codec_context_(open_codec(*context, AVMEDIA_TYPE_AUDIO, index_))\r
-               , resampler_(format_desc.audio_channels,        codec_context_->channels,\r
+               , resampler_(codec_context_->channels,          codec_context_->channels,\r
                                         format_desc.audio_sample_rate, codec_context_->sample_rate,\r
                                         AV_SAMPLE_FMT_S32,                             codec_context_->sample_fmt)\r
                , buffer1_(AVCODEC_MAX_AUDIO_FRAME_SIZE*2)\r
                , nb_frames_(0)//context->streams[index_]->nb_frames)\r
-       {               \r
-               file_frame_number_ = 0;   \r
+               , channel_layout_(get_audio_channel_layout(*codec_context_, custom_channel_order))\r
+       {\r
+               file_frame_number_ = 0;\r
+\r
+               CASPAR_LOG(debug) << print() \r
+                               << " Selected channel layout " << channel_layout_.name;\r
        }\r
 \r
        void push(const std::shared_ptr<AVPacket>& packet)\r
@@ -147,12 +153,13 @@ public:
        }\r
 };\r
 \r
-audio_decoder::audio_decoder(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc) : impl_(new implementation(context, format_desc)){}\r
+audio_decoder::audio_decoder(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc, const std::wstring& custom_channel_order) : impl_(new implementation(context, format_desc, custom_channel_order)){}\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::shared_ptr<core::audio_buffer> audio_decoder::poll(){return impl_->poll();}\r
 uint32_t audio_decoder::nb_frames() const{return impl_->nb_frames();}\r
 uint32_t audio_decoder::file_frame_number() const{return impl_->file_frame_number_;}\r
+const core::channel_layout& audio_decoder::channel_layout() const { return impl_->channel_layout_; }\r
 std::wstring audio_decoder::print() const{return impl_->print();}\r
 \r
 }}
\ No newline at end of file
index bb5c120925b841a0db55c45919692a64f5bd9f9b..7f5c07d5586369f53e055ef687920ac80ba43532 100644 (file)
@@ -35,6 +35,7 @@ namespace caspar {
 namespace core {\r
 \r
 struct video_format_desc;\r
+struct channel_layout;\r
 \r
 }\r
 \r
@@ -43,7 +44,7 @@ namespace ffmpeg {
 class audio_decoder : boost::noncopyable\r
 {\r
 public:\r
-       explicit audio_decoder(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc);\r
+       explicit audio_decoder(const safe_ptr<AVFormatContext>& context, const core::video_format_desc& format_desc, const std::wstring& custom_channel_order);\r
        \r
        bool ready() const;\r
        void push(const std::shared_ptr<AVPacket>& packet);\r
@@ -53,6 +54,8 @@ public:
        \r
        uint32_t file_frame_number() const;\r
 \r
+       const core::channel_layout& channel_layout() const;\r
+\r
        std::wstring print() const;\r
 private:\r
        struct implementation;\r
index 442d460b5da6c2584a4889a5aaecee2a697c0cdb..4a0cd37bd285356155b558a0575d6629da7e8a88 100644 (file)
@@ -92,7 +92,7 @@ struct ffmpeg_producer : public core::frame_producer
        uint32_t                                                                                                        file_frame_number_;\r
                \r
 public:\r
-       explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, const std::wstring& filter, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode)\r
+       explicit ffmpeg_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, const std::wstring& filter, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const std::wstring& custom_channel_order)\r
                : filename_(filename)\r
                , frame_factory_(frame_factory)         \r
                , format_desc_(frame_factory->get_video_format_desc())\r
@@ -128,11 +128,14 @@ public:
                        }\r
                }\r
 \r
+               core::channel_layout audio_channel_layout = core::default_channel_layout_repository().get_by_name(L"STEREO");\r
+\r
                if (!thumbnail_mode_)\r
                {\r
                        try\r
                        {\r
-                               audio_decoder_.reset(new audio_decoder(input_.context(), frame_factory->get_video_format_desc()));\r
+                               audio_decoder_.reset(new audio_decoder(input_.context(), frame_factory->get_video_format_desc(), custom_channel_order));\r
+                               audio_channel_layout = audio_decoder_->channel_layout();\r
                                CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();\r
                        }\r
                        catch(averror_stream_not_found&)\r
@@ -149,7 +152,7 @@ public:
                if(!video_decoder_ && !audio_decoder_)\r
                        BOOST_THROW_EXCEPTION(averror_stream_not_found() << msg_info("No streams found"));\r
 \r
-               muxer_.reset(new frame_muxer(fps_, frame_factory, thumbnail_mode_, filter));\r
+               muxer_.reset(new frame_muxer(fps_, frame_factory, thumbnail_mode_, audio_channel_layout, filter));\r
        }\r
 \r
        // frame_producer\r
@@ -455,15 +458,16 @@ safe_ptr<core::frame_producer> create_producer(
        if(filename.empty())\r
                return core::frame_producer::empty();\r
        \r
-       auto loop               = boost::range::find(params, L"LOOP") != params.end();\r
-       auto start              = get_param(L"SEEK", params, static_cast<uint32_t>(0));\r
-       auto length             = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());\r
-       auto filter_str = get_param(L"FILTER", params, L"");    \r
-               \r
+       auto loop                                       = boost::range::find(params, L"LOOP") != params.end();\r
+       auto start                                      = get_param(L"SEEK", params, static_cast<uint32_t>(0));\r
+       auto length                                     = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());\r
+       auto filter_str                         = get_param(L"FILTER", params, L"");    \r
+       auto custom_channel_order       = get_param(L"CHANNEL_LAYOUT", params, L"");\r
+\r
        boost::replace_all(filter_str, L"DEINTERLACE", L"YADIF=0:-1");\r
        boost::replace_all(filter_str, L"DEINTERLACE_BOB", L"YADIF=1:-1");\r
        \r
-       return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, filter_str, loop, start, length, false));\r
+       return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, filter_str, loop, start, length, false, custom_channel_order));\r
 }\r
 \r
 safe_ptr<core::frame_producer> create_thumbnail_producer(\r
@@ -484,7 +488,7 @@ safe_ptr<core::frame_producer> create_thumbnail_producer(
        auto length             = std::numeric_limits<uint32_t>::max();\r
        auto filter_str = L"";\r
                \r
-       return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, filter_str, loop, start, length, true));\r
+       return create_producer_destroy_proxy(make_safe<ffmpeg_producer>(frame_factory, filename, filter_str, loop, start, length, true, L""));\r
 }\r
 \r
 }}
\ No newline at end of file
index 52d837cc656f3c8967ce07abe93d61205a51a5f8..b733e924a76cbb26dd886d84861a6cbffb8c7fca 100644 (file)
@@ -32,6 +32,7 @@
 #include <core/producer/frame/pixel_format.h>\r
 #include <core/producer/frame/frame_factory.h>\r
 #include <core/mixer/write_frame.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <common/env.h>\r
 #include <common/exception/exceptions.h>\r
@@ -83,8 +84,14 @@ struct frame_muxer::implementation : boost::noncopyable
        const std::wstring                                                              filter_str_;\r
        const bool                                                                              thumbnail_mode_;\r
        bool                                                                                    force_deinterlacing_;\r
+       const core::channel_layout                                              audio_channel_layout_;\r
                \r
-       implementation(double in_fps, const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filter_str, bool thumbnail_mode)\r
+       implementation(\r
+                       double in_fps,\r
+                       const safe_ptr<core::frame_factory>& frame_factory,\r
+                       const std::wstring& filter_str,\r
+                       bool thumbnail_mode,\r
+                       const core::channel_layout& audio_channel_layout)\r
                : display_mode_(display_mode::invalid)\r
                , in_fps_(in_fps)\r
                , format_desc_(frame_factory->get_video_format_desc())\r
@@ -95,6 +102,7 @@ struct frame_muxer::implementation : boost::noncopyable
                , filter_str_(filter_str)\r
                , thumbnail_mode_(thumbnail_mode)\r
                , force_deinterlacing_(false)\r
+               , audio_channel_layout_(audio_channel_layout)\r
        {\r
                video_streams_.push(std::queue<safe_ptr<write_frame>>());\r
                audio_streams_.push(core::audio_buffer());\r
@@ -115,7 +123,7 @@ struct frame_muxer::implementation : boost::noncopyable
                }\r
                else if(video_frame == empty_video())\r
                {\r
-                       video_streams_.back().push(make_safe<core::write_frame>(this));\r
+                       video_streams_.back().push(make_safe<core::write_frame>(this, audio_channel_layout_));\r
                        display_mode_ = display_mode::simple;\r
                }\r
                else\r
@@ -144,7 +152,7 @@ struct frame_muxer::implementation : boost::noncopyable
                                if(video_frame->format == PIX_FMT_GRAY8 && format == CASPAR_PIX_FMT_LUMA)\r
                                        av_frame->format = format;\r
 \r
-                               video_streams_.back().push(make_write_frame(this, av_frame, frame_factory_, hints));\r
+                               video_streams_.back().push(make_write_frame(this, av_frame, frame_factory_, hints, audio_channel_layout_));\r
                        }\r
                }\r
 \r
@@ -163,14 +171,14 @@ struct frame_muxer::implementation : boost::noncopyable
                }\r
                else if(audio == empty_audio())\r
                {\r
-                       boost::range::push_back(audio_streams_.back(), core::audio_buffer(audio_cadence_.front() * format_desc_.audio_channels, 0));\r
+                       boost::range::push_back(audio_streams_.back(), core::audio_buffer(audio_cadence_.front() * audio_channel_layout_.num_channels, 0));\r
                }\r
                else\r
                {\r
                        boost::range::push_back(audio_streams_.back(), *audio);\r
                }\r
 \r
-               if(audio_streams_.back().size() > 32*audio_cadence_.front() * format_desc_.audio_channels)\r
+               if(audio_streams_.back().size() > 32*audio_cadence_.front() * audio_channel_layout_.num_channels)\r
                        BOOST_THROW_EXCEPTION(invalid_operation() << source_info("frame_muxer") << msg_info("audio-stream overflow. This can be caused by incorrect frame-rate. Check clip meta-data."));\r
        }\r
        \r
@@ -202,9 +210,9 @@ struct frame_muxer::implementation : boost::noncopyable
                switch(display_mode_)\r
                {\r
                case display_mode::duplicate:                                   \r
-                       return audio_streams_.front().size()/2 >= audio_cadence_.front() * format_desc_.audio_channels;\r
+                       return audio_streams_.front().size()/2 >= audio_cadence_.front() * audio_channel_layout_.num_channels;\r
                default:                                                                                \r
-                       return audio_streams_.front().size() >= audio_cadence_.front() * format_desc_.audio_channels;\r
+                       return audio_streams_.front().size() >= audio_cadence_.front() * audio_channel_layout_.num_channels;\r
                }\r
        }\r
                \r
@@ -279,10 +287,10 @@ struct frame_muxer::implementation : boost::noncopyable
 \r
        core::audio_buffer pop_audio()\r
        {\r
-               CASPAR_VERIFY(audio_streams_.front().size() >= audio_cadence_.front() * format_desc_.audio_channels);\r
+               CASPAR_VERIFY(audio_streams_.front().size() >= audio_cadence_.front() * audio_channel_layout_.num_channels);\r
 \r
                auto begin = audio_streams_.front().begin();\r
-               auto end   = begin + (audio_cadence_.front() * format_desc_.audio_channels);\r
+               auto end   = begin + (audio_cadence_.front() * audio_channel_layout_.num_channels);\r
 \r
                core::audio_buffer samples(begin, end);\r
                audio_streams_.front().erase(begin, end);\r
@@ -347,7 +355,7 @@ struct frame_muxer::implementation : boost::noncopyable
                                filter_.push(frame);\r
                                auto av_frame = filter_.poll();\r
                                if(av_frame)                                                    \r
-                                       video_streams_.back().push(make_write_frame(this, make_safe_ptr(av_frame), frame_factory_, 0));\r
+                                       video_streams_.back().push(make_write_frame(this, make_safe_ptr(av_frame), frame_factory_, 0, audio_channel_layout_));\r
                        }\r
                        filter_ = filter(filter_str);\r
                        if (!thumbnail_mode_)\r
@@ -378,8 +386,13 @@ struct frame_muxer::implementation : boost::noncopyable
        }\r
 };\r
 \r
-frame_muxer::frame_muxer(double in_fps, const safe_ptr<core::frame_factory>& frame_factory, bool thumbnail_mode, const std::wstring& filter)\r
-       : impl_(new implementation(in_fps, frame_factory, filter, thumbnail_mode)){}\r
+frame_muxer::frame_muxer(\r
+               double in_fps,\r
+               const safe_ptr<core::frame_factory>& frame_factory,\r
+               bool thumbnail_mode,\r
+               const core::channel_layout& audio_channel_layout,\r
+               const std::wstring& filter)\r
+       : impl_(new implementation(in_fps, frame_factory, filter, thumbnail_mode, audio_channel_layout)){}\r
 void frame_muxer::push(const std::shared_ptr<AVFrame>& video_frame, int hints){impl_->push(video_frame, hints);}\r
 void frame_muxer::push(const std::shared_ptr<core::audio_buffer>& audio_samples){return impl_->push(audio_samples);}\r
 std::shared_ptr<basic_frame> frame_muxer::poll(){return impl_->poll();}\r
index 6d3671bc38d5d2251706b11b1ba93b27359dfcb5..28d9dd853f68b653ccce4a5c88e0fb01a70dc16b 100644 (file)
@@ -40,6 +40,7 @@ namespace core {
 class write_frame;\r
 class basic_frame;\r
 struct frame_factory;\r
+struct channel_layout;\r
 \r
 }\r
 \r
@@ -48,7 +49,12 @@ namespace ffmpeg {
 class frame_muxer : boost::noncopyable\r
 {\r
 public:\r
-       frame_muxer(double in_fps, const safe_ptr<core::frame_factory>& frame_factory, bool thumbnail_mode, const std::wstring& filter = L"");\r
+       frame_muxer(\r
+                       double in_fps,\r
+                       const safe_ptr<core::frame_factory>& frame_factory,\r
+                       bool thumbnail_mode,\r
+                       const core::channel_layout& audio_channel_layout,\r
+                       const std::wstring& filter = L"");\r
        \r
        void push(const std::shared_ptr<AVFrame>& video_frame, int hints = 0);\r
        void push(const std::shared_ptr<core::audio_buffer>& audio_samples);\r
index ceaa8393c43483a05d4aecaf6664085375de4b79..f1f34c08e8b3760694c26628d64a63357bd54215 100644 (file)
@@ -35,6 +35,7 @@
 #include <core/producer/frame/frame_factory.h>\r
 #include <core/producer/frame_producer.h>\r
 #include <core/mixer/write_frame.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <common/exception/exceptions.h>\r
 #include <common/utility/assert.h>\r
@@ -44,6 +45,7 @@
 \r
 #include <boost/filesystem.hpp>\r
 #include <boost/lexical_cast.hpp>\r
+#include <boost/algorithm/string.hpp>\r
 \r
 #if defined(_MSC_VER)\r
 #pragma warning (push)\r
@@ -171,12 +173,12 @@ int make_alpha_format(int format)
        }\r
 }\r
 \r
-safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVFrame>& decoded_frame, const safe_ptr<core::frame_factory>& frame_factory, int hints)\r
+safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVFrame>& decoded_frame, const safe_ptr<core::frame_factory>& frame_factory, int hints, const core::channel_layout& audio_channel_layout)\r
 {                      \r
        static tbb::concurrent_unordered_map<int64_t, tbb::concurrent_queue<std::shared_ptr<SwsContext>>> sws_contexts_;\r
        \r
        if(decoded_frame->width < 1 || decoded_frame->height < 1)\r
-               return make_safe<core::write_frame>(tag);\r
+               return make_safe<core::write_frame>(tag, audio_channel_layout);\r
 \r
        const auto width  = decoded_frame->width;\r
        const auto height = decoded_frame->height;\r
@@ -207,7 +209,7 @@ safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVF
                \r
                auto target_desc = get_pixel_format_desc(target_pix_fmt, width, height);\r
 \r
-               write = frame_factory->create_frame(tag, target_desc);\r
+               write = frame_factory->create_frame(tag, target_desc, audio_channel_layout);\r
                write->set_type(get_mode(*decoded_frame));\r
 \r
                std::shared_ptr<SwsContext> sws_context;\r
@@ -258,7 +260,7 @@ safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVF
        }\r
        else\r
        {\r
-               write = frame_factory->create_frame(tag, desc);\r
+               write = frame_factory->create_frame(tag, desc, audio_channel_layout);\r
                write->set_type(get_mode(*decoded_frame));\r
 \r
                for(int n = 0; n < static_cast<int>(desc.planes.size()); ++n)\r
@@ -516,6 +518,42 @@ std::wstring probe_stem(const std::wstring stem, const std::vector<std::wstring>
        }\r
        return L"";\r
 }\r
+\r
+core::channel_layout get_audio_channel_layout(\r
+               const AVCodecContext& context, const std::wstring& custom_channel_order)\r
+{\r
+       if (!custom_channel_order.empty())\r
+       {\r
+               auto layout = core::create_custom_channel_layout(\r
+                               custom_channel_order,\r
+                               core::default_channel_layout_repository());\r
+\r
+               layout.num_channels = context.channels;\r
+\r
+               return layout;\r
+       }\r
+\r
+       int64_t ch_layout = context.channel_layout;\r
+\r
+       if (ch_layout == 0)\r
+               ch_layout = av_get_default_channel_layout(context.channels);\r
+\r
+       switch (ch_layout) // TODO: refine this auto-detection\r
+       {\r
+       case AV_CH_LAYOUT_MONO:\r
+               return core::default_channel_layout_repository().get_by_name(L"MONO");\r
+       case AV_CH_LAYOUT_STEREO:\r
+               return core::default_channel_layout_repository().get_by_name(L"STEREO");\r
+       case AV_CH_LAYOUT_5POINT1:\r
+       case AV_CH_LAYOUT_5POINT1_BACK:\r
+               return core::default_channel_layout_repository().get_by_name(L"SMPTE");\r
+       case AV_CH_LAYOUT_7POINT1:\r
+               return core::default_channel_layout_repository().get_by_name(L"DOLBYE");\r
+       }\r
+\r
+       return core::create_unspecified_layout(context.channels);\r
+}\r
+\r
 //\r
 //void av_dup_frame(AVFrame* frame)\r
 //{\r
index c830427feaa51d611e07f7e69390d2eea372ccab..4e8e3f2d1624c80732a08b256f783c76b0f4f9af 100644 (file)
@@ -41,6 +41,7 @@ namespace core {
 struct pixel_format_desc;\r
 class write_frame;\r
 struct frame_factory;\r
+struct channel_layout;\r
 \r
 }\r
 \r
@@ -57,7 +58,7 @@ static const int CASPAR_PIX_FMT_LUMA = 10; // Just hijack some unual pixel forma
 \r
 core::field_mode::type         get_mode(const AVFrame& frame);\r
 int                                                    make_alpha_format(int format); // NOTE: Be careful about CASPAR_PIX_FMT_LUMA, change it to PIX_FMT_GRAY8 if you want to use the frame inside some ffmpeg function.\r
-safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVFrame>& decoded_frame, const safe_ptr<core::frame_factory>& frame_factory, int hints);\r
+safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVFrame>& decoded_frame, const safe_ptr<core::frame_factory>& frame_factory, int hints, const core::channel_layout& audio_channel_layout);\r
 \r
 safe_ptr<AVPacket> create_packet();\r
 \r
@@ -75,4 +76,6 @@ std::wstring probe_stem(const std::wstring stem, const std::vector<std::wstring>
 bool is_valid_file(const std::wstring filename, const std::vector<std::wstring>& invalid_exts);\r
 bool is_valid_file(const std::wstring filename);\r
 \r
+core::channel_layout get_audio_channel_layout(const AVCodecContext& context, const std::wstring& custom_channel_order);\r
+\r
 }}
\ No newline at end of file
index 40a1cc2b29684f1af8fb3cfb20973e16f39988bf..1b3b5360dc62434aa91d6fa181341e8b1e2932ce 100644 (file)
@@ -28,6 +28,7 @@
 \r
 #include <core/producer/frame/frame_factory.h>\r
 #include <core/mixer/write_frame.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 \r
 #include <common/env.h>\r
 \r
@@ -52,9 +53,9 @@ std::wstring get_cg_version()
                struct dummy_factory : public core::frame_factory\r
                {\r
                \r
-                       virtual safe_ptr<core::write_frame> create_frame(const void* video_stream_tag, const core::pixel_format_desc& desc) \r
+                       virtual safe_ptr<core::write_frame> create_frame(const void* video_stream_tag, const core::pixel_format_desc& desc, const core::channel_layout& layout\r
                        {\r
-                               return make_safe<core::write_frame>(nullptr);\r
+                               return make_safe<core::write_frame>(nullptr, layout);\r
                        }\r
        \r
                        virtual core::video_format_desc get_video_format_desc() const\r
index f1e8a4e4c5aa2c5c9abe5e33a0d19dde3d16bff1..89a1c15a9ea0c57d962102125a206f02e6c27262 100644 (file)
@@ -60,10 +60,14 @@ struct oal_consumer : public core::frame_consumer,  public sf::SoundStream
        core::audio_buffer                                                                      temp;
 
        core::video_format_desc                                                         format_desc_;
+       core::channel_layout                                                            channel_layout_;
 public:
        oal_consumer() 
                : container_(16)
                , channel_index_(-1)
+               , channel_layout_(
+                               core::default_channel_layout_repository().get_by_name(
+                                               L"STEREO"))
        {
                graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
                graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
@@ -104,7 +108,35 @@ public:
        
        virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override
        {
-               auto buffer = std::make_shared<audio_buffer_16>(core::audio_32_to_16(frame->audio_data()));
+               std::shared_ptr<audio_buffer_16> buffer;
+
+               if (core::needs_rearranging(
+                               frame->multichannel_view(),
+                               channel_layout_,
+                               channel_layout_.num_channels))
+               {
+                       core::audio_buffer downmixed;
+                       downmixed.resize(
+                                       frame->multichannel_view().num_samples() 
+                                                       * channel_layout_.num_channels,
+                                       0);
+
+                       auto dest_view = core::make_multichannel_view<int32_t>(
+                                       downmixed.begin(), downmixed.end(), channel_layout_);
+
+                       core::rearrange_or_rearrange_and_mix(
+                                       frame->multichannel_view(),
+                                       dest_view,
+                                       core::default_mix_config_repository());
+
+                       buffer = std::make_shared<audio_buffer_16>(
+                                       core::audio_32_to_16(downmixed));
+               }
+               else
+               {
+                       buffer = std::make_shared<audio_buffer_16>(
+                                       core::audio_32_to_16(frame->audio_data()));
+               }
 
                if (!input_.try_push(buffer))
                        graph_->set_tag("dropped-frame");
index 77bda2f9071cfe965590acdb381f5f4518efef80..8b27a5777cfecb4b28bd0aae2ecb7b1bd100d486 100644 (file)
@@ -1541,7 +1541,7 @@ bool ThumbnailCommand::DoExecuteGenerate()
        }\r
        else\r
        {\r
-               SetReplyString(L"500 THUMBNAIL GENERATE ERROR\r\n");\r
+               SetReplyString(L"501 THUMBNAIL GENERATE ERROR\r\n");\r
                return false;\r
        }\r
 }\r
@@ -1558,7 +1558,7 @@ bool ThumbnailCommand::DoExecuteGenerateAll()
        }\r
        else\r
        {\r
-               SetReplyString(L"500 THUMBNAIL GENERATE_ALL ERROR\r\n");\r
+               SetReplyString(L"501 THUMBNAIL GENERATE_ALL ERROR\r\n");\r
                return false;\r
        }\r
 }\r
index 8bc761456f426c3b940290ce54d16dc89f689afe..14353e17d1f9410f1b499e5c1e29a2e528326847 100644 (file)
@@ -10,6 +10,7 @@
   <channels>\r
     <channel>\r
         <video-mode>PAL</video-mode>\r
+        <channel-layout>stereo</channel-layout>\r
         <consumers>\r
           <screen>\r
             <device>1</device>\r
       <protocol>OSC</protocol>\r
     </udp>\r
   </controllers>\r
+  <audio>\r
+    <channel-layouts>\r
+    </channel-layouts>\r
+    <mix-configs>\r
+    </mix-configs>\r
+  </audio>\r
 </configuration>\r
 \r
 <!--\r
 <channels>\r
     <channel>\r
         <video-mode> PAL [PAL|NTSC|576p2500|720p2398|720p2400|720p2500|720p5000|720p2997|720p5994|720p3000|720p6000|1080p2398|1080p2400|1080i5000|1080i5994|1080i6000|1080p2500|1080p2997|1080p3000|1080p5000|1080p5994|1080p6000|2k2398|2k2400|2k2500|4k2398|4k2400|4k2500|4k2997|4k3000] </video-mode>\r
+        <channel-layout>stereo [mono|stereo|dts|dolbye|dolbydigital|smpte|passthru]</channel-layout>\r
         <consumers>\r
             <decklink>\r
                 <device>[1..]</device>\r
                 <embedded-audio>false [true|false]</embedded-audio>\r
+                <channel-layout>stereo [mono|stereo|dts|dolbye|dolbydigital|smpte|passthru]</channel-layout>\r
                 <latency>normal [normal|low|default]</latency>\r
                 <keyer>external [external|internal|default]</keyer>\r
                 <key-only>false [true|false]</key-only>\r
@@ -73,6 +82,7 @@
             <bluefish>\r
                 <device>[1..]</device>\r
                 <embedded-audio>false [true|false]</embedded-audio>\r
+                <channel-layout>stereo [mono|stereo|dts|dolbye|dolbydigital|smpte|passthru]</channel-layout>\r
                 <key-only>false [true|false]</key-only>\r
             </bluefish>\r
             <system-audio></system-audio>\r
         </consumers>\r
     </channel>\r
 </channels>\r
+<audio>\r
+  <channel-layouts>\r
+    <mono>\r
+      <type>1.0</type>\r
+      <num-channels>1</num-channels>\r
+      <channels>C</channels>\r
+    </mono>\r
+    <stereo>\r
+      <type>2.0</type>\r
+      <num-channels>2</num-channels>\r
+      <channels>L R</channels>\r
+    </stereo>\r
+    <dts>\r
+      <type>5.1</type>\r
+      <num-channels>6</num-channels>\r
+      <channels>C L R Ls Rs LFE</channels>\r
+    </dts>\r
+    <dolbye>\r
+      <type>5.1+stereomix</type>\r
+      <num-channels>8</num-channels>\r
+      <channels>L R C LFE Ls Rs Lmix Rmix</channels>\r
+    </dolbye>\r
+    <dolbydigital>\r
+      <type>5.1</type>\r
+      <num-channels>6</num-channels>\r
+      <channels>L C R Ls Rs LFE</channels>\r
+    </dolbydigital>\r
+    <smpte>\r
+      <type>5.1</type>\r
+      <num-channels>6</num-channels>\r
+      <channels>L R C LFE Ls Rs</channels>\r
+    </smpte>\r
+    <passthru>\r
+      <type>16ch</type>\r
+      <num-channels>16</num-channels>\r
+      <channels />\r
+    </passthru>\r
+  </channel-layouts>\r
+  <mix-configs>\r
+    <mix-config>\r
+      <from>1.0</from>\r
+      <to>2.0</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>C L 1.0</mapping>\r
+        <mapping>C R 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>1.0</from>\r
+      <to>5.1</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>C L 1.0</mapping>\r
+        <mapping>C R 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>1.0</from>\r
+      <to>5.1+stereomix</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>C L    1.0</mapping>\r
+        <mapping>C R    1.0</mapping>\r
+        <mapping>C Lmix 1.0</mapping>\r
+        <mapping>C Rmix 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>2.0</from>\r
+      <to>1.0</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>L C 1.0</mapping>\r
+        <mapping>R C 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>2.0</from>\r
+      <to>5.1</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>L L 1.0</mapping>\r
+        <mapping>R R 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>2.0</from>\r
+      <to>5.1+stereomix</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>L L    1.0</mapping>\r
+        <mapping>R R    1.0</mapping>\r
+        <mapping>L Lmix 1.0</mapping>\r
+        <mapping>R Rmix 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>5.1</from>\r
+      <to>1.0</to>\r
+      <mix>average</mix>\r
+      <mappings>\r
+        <mapping>L  C 1.0</mapping>\r
+        <mapping>R  C 1.0</mapping>\r
+        <mapping>C  C 0.707</mapping>\r
+        <mapping>Ls C 0.707</mapping>\r
+        <mapping>Rs C 0.707</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>5.1</from>\r
+      <to>2.0</to>\r
+      <mix>average</mix>\r
+      <mappings>\r
+        <mapping>L  L 1.0</mapping>\r
+        <mapping>R  R 1.0</mapping>\r
+        <mapping>C  L 0.707</mapping>\r
+        <mapping>C  R 0.707</mapping>\r
+        <mapping>Ls L 0.707</mapping>\r
+        <mapping>Rs R 0.707</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>5.1</from>\r
+      <to>5.1+stereomix</to>\r
+      <mix>average</mix>\r
+      <mappings>\r
+        <mapping>L   L   1.0</mapping>\r
+        <mapping>R   R   1.0</mapping>\r
+        <mapping>C   C   1.0</mapping>\r
+        <mapping>Ls  Ls  1.0</mapping>\r
+        <mapping>Rs  Rs  1.0</mapping>\r
+        <mapping>LFE LFE 1.0</mapping>\r
+\r
+        <mapping>L  Lmix 1.0</mapping>\r
+        <mapping>R  Rmix 1.0</mapping>\r
+        <mapping>C  Lmix 0.707</mapping>\r
+        <mapping>C  Rmix 0.707</mapping>\r
+        <mapping>Ls Lmix 0.707</mapping>\r
+        <mapping>Rs Rmix 0.707</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>5.1+stereomix</from>\r
+      <to>1.0</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>Lmix C 1.0</mapping>\r
+        <mapping>Rmix C 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>5.1+stereomix</from>\r
+      <to>2.0</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>Lmix L 1.0</mapping>\r
+        <mapping>Rmix R 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+    <mix-config>\r
+      <from>5.1+stereomix</from>\r
+      <to>5.1</to>\r
+      <mix>add</mix>\r
+      <mappings>\r
+        <mapping>L   L   1.0</mapping>\r
+        <mapping>R   R   1.0</mapping>\r
+        <mapping>C   C   1.0</mapping>\r
+        <mapping>Ls  Ls  1.0</mapping>\r
+        <mapping>Rs  Rs  1.0</mapping>\r
+        <mapping>LFE LFE 1.0</mapping>\r
+      </mappings>\r
+    </mix-config>\r
+  </mix-configs>\r
+</audio>\r
 -->\r
index d9335a6ad10bf0f15863235fd7761bad60835c77..ed5d2c93bc912a8ae1422099dca7985dc4335a55 100644 (file)
@@ -30,6 +30,7 @@
 #include <common/filesystem/polling_filesystem_monitor.h>\r
 \r
 #include <core/mixer/gpu/ogl_device.h>\r
+#include <core/mixer/audio/audio_util.h>\r
 #include <core/video_channel.h>\r
 #include <core/producer/stage.h>\r
 #include <core/consumer/output.h>\r
@@ -83,6 +84,8 @@ struct server::implementation : boost::noncopyable
                : shutdown_server_now_(shutdown_server_now)\r
                , ogl_(ogl_device::create())\r
        {                       \r
+               setup_audio(env::properties());\r
+\r
                ffmpeg::init();\r
                CASPAR_LOG(info) << L"Initialized ffmpeg module.";\r
                                                          \r
@@ -120,6 +123,18 @@ struct server::implementation : boost::noncopyable
                async_servers_.clear();\r
                channels_.clear();\r
        }\r
+\r
+       void setup_audio(const boost::property_tree::wptree& pt)\r
+       {\r
+               register_default_channel_layouts(default_channel_layout_repository());\r
+               register_default_mix_configs(default_mix_config_repository());\r
+               parse_channel_layouts(\r
+                               default_channel_layout_repository(),\r
+                               pt.get_child(L"configuration.audio.channel-layouts"));\r
+               parse_mix_configs(\r
+                               default_mix_config_repository(),\r
+                               pt.get_child(L"configuration.audio.mix-configs"));\r
+       }\r
                                \r
        void setup_channels(const boost::property_tree::wptree& pt)\r
        {   \r
@@ -129,8 +144,10 @@ struct server::implementation : boost::noncopyable
                        auto format_desc = video_format_desc::get(widen(xml_channel.second.get(L"video-mode", L"PAL")));                \r
                        if(format_desc.format == video_format::invalid)\r
                                BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Invalid video-mode."));\r
+                       auto audio_channel_layout = default_channel_layout_repository().get_by_name(\r
+                                       boost::to_upper_copy(xml_channel.second.get(L"channel-layout", L"STEREO")));\r
                        \r
-                       channels_.push_back(make_safe<video_channel>(channels_.size()+1, format_desc, ogl_));\r
+                       channels_.push_back(make_safe<video_channel>(channels_.size()+1, format_desc, ogl_, audio_channel_layout));\r
                        \r
                        channels_.back()->monitor_output().link_target(&monitor_subject_);\r
 \r
@@ -161,7 +178,7 @@ struct server::implementation : boost::noncopyable
 \r
                // Dummy diagnostics channel\r
                if(env::properties().get(L"configuration.channel-grid", false))\r
-                       channels_.push_back(make_safe<video_channel>(channels_.size()+1, core::video_format_desc::get(core::video_format::x576p2500), ogl_));\r
+                       channels_.push_back(make_safe<video_channel>(channels_.size()+1, core::video_format_desc::get(core::video_format::x576p2500), ogl_, default_channel_layout_repository().get_by_name(L"STEREO")));\r
        }\r
                \r
        void setup_controllers(const boost::property_tree::wptree& pt)\r