[vlc-commits] sout: sdi: add support for audio pairs remapping

Francois Cartegnie git at videolan.org
Wed Sep 5 10:36:45 CEST 2018


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Fri Aug 31 18:21:15 2018 +0200| [989a9db6668ca05d158bd6197274bfca81b7c121] | committer: Francois Cartegnie

sout: sdi: add support for audio pairs remapping

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=989a9db6668ca05d158bd6197274bfca81b7c121
---

 modules/stream_out/sdi/DBMSDIOutput.cpp      |  11 +-
 modules/stream_out/sdi/DBMSDIOutput.hpp      |   1 -
 modules/stream_out/sdi/SDIAudioMultiplex.cpp | 179 +++++++++++++++++++++++++++
 modules/stream_out/sdi/SDIAudioMultiplex.hpp |  10 ++
 modules/stream_out/sdi/SDIOutput.cpp         |  31 +++--
 modules/stream_out/sdi/SDIOutput.hpp         |   2 +-
 modules/stream_out/sdi/sdiout.cpp            |  10 ++
 7 files changed, 220 insertions(+), 24 deletions(-)

diff --git a/modules/stream_out/sdi/DBMSDIOutput.cpp b/modules/stream_out/sdi/DBMSDIOutput.cpp
index d2e602fd36..0818e4fc03 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.cpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.cpp
@@ -54,7 +54,6 @@ DBMSDIOutput::~DBMSDIOutput()
     if(video.pic_nosignal)
         picture_Release(video.pic_nosignal);
     es_format_Clean(&video.configuredfmt);
-    es_format_Clean(&audio.configuredfmt);
     if(p_output)
     {
         BMDTimeValue out;
@@ -274,15 +273,6 @@ int DBMSDIOutput::ConfigureAudio(const audio_format_t *)
 {
     HRESULT result;
 
-    audio.configuredfmt.i_codec =
-    audio.configuredfmt.audio.i_format = VLC_CODEC_S16N;
-    audio.configuredfmt.audio.i_channels = 2;
-    audio.configuredfmt.audio.i_physical_channels = AOUT_CHANS_STEREO;
-    audio.configuredfmt.audio.i_rate = 48000;
-    audio.configuredfmt.audio.i_bitspersample = 16;
-    audio.configuredfmt.audio.i_blockalign = 2 * 16 / 8;
-    //audio.configuredfmt.audio.i_frame_length = BLOCK_SIZE_BYTES;
-
     if(FAKE_DRIVER)
         return VLC_SUCCESS;
 
@@ -302,6 +292,7 @@ int DBMSDIOutput::ConfigureAudio(const audio_format_t *)
                     maxchannels,
                     bmdAudioOutputStreamTimestamped);
         CHECK("Could not start audio output");
+        audio.b_configured = true;
     }
     return VLC_SUCCESS;
 
diff --git a/modules/stream_out/sdi/DBMSDIOutput.hpp b/modules/stream_out/sdi/DBMSDIOutput.hpp
index 7c1de10bf2..13a65fd29f 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.hpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.hpp
@@ -53,7 +53,6 @@ namespace sdi_sout
             vlc_tick_t lasttimestamp;
             /* XXX: workaround card clock drift */
             vlc_tick_t offset;
-
             bool b_running;
             int Start();
             const char *ErrorToString(long i_code);
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.cpp b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
index 35993947c5..4369932f64 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.cpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
@@ -22,7 +22,10 @@
 #endif
 
 #include "SDIAudioMultiplex.hpp"
+#include <vlc_es.h>
 #include <limits>
+#include <cstring>
+#include <algorithm>
 
 using namespace sdi_sout;
 
@@ -52,10 +55,39 @@ void * SDIAudioMultiplexBuffer::Dequeue()
     return NULL;
 }
 
+static void ConfigureChannels(unsigned i, es_format_t *fmt)
+{
+    if( i>=8 )
+    {
+        i = 8;
+        fmt->audio.i_physical_channels = AOUT_CHANS_7_1;
+    }
+    else if( i>2 )
+    {
+        i = 6;
+        fmt->audio.i_physical_channels = AOUT_CHANS_5_1;
+    }
+    else
+    {
+        fmt->audio.i_physical_channels = AOUT_CHANS_STEREO;
+    }
+    fmt->audio.i_channels = i;
+    fmt->audio.i_blockalign = i * 16 / 8;
+}
+
 SDIAudioMultiplexConfig::Mapping::Mapping(const StreamID &id)
     : id(id)
 {
+    es_format_Init(&fmt, AUDIO_ES, VLC_CODEC_S16N);
+    fmt.audio.i_format = VLC_CODEC_S16N;
+    fmt.audio.i_rate = 48000;
+    fmt.audio.i_bitspersample = 16;
+    ConfigureChannels(2, &fmt);
+}
 
+SDIAudioMultiplexConfig::Mapping::~Mapping()
+{
+    es_format_Clean(&fmt);
 }
 
 SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(uint8_t channels)
@@ -67,6 +99,7 @@ SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(uint8_t channels)
         framewidth = 4;
     else
         framewidth = 1;
+    b_accept_any = true;
 }
 
 SDIAudioMultiplexConfig::~SDIAudioMultiplexConfig()
@@ -85,6 +118,98 @@ void SDIAudioMultiplexConfig::setSubFrameSlotUsed(uint8_t i)
     subframeslotbitmap |= (1 << i);
 }
 
+void SDIAudioMultiplexConfig::parseConfiguration(vlc_object_t *obj, const char *psz)
+{
+    char *name = NULL;
+    char *psz_in = (char*)psz;
+    config_chain_t *p_config_chain = NULL;
+    while(psz_in)
+    {
+        char *psz_next = config_ChainCreate(&name, &p_config_chain, psz_in);
+        if(name)
+        {
+            if(!std::strcmp(name, "only"))
+            {
+                b_accept_any = false;
+                msg_Dbg(obj, "only accepting declared streams");
+            }
+            else /* try mapping decl */
+            {
+                int i_id = -1;
+                int i_seqid = -1;
+                int *pi_id = &i_seqid;
+                const char *psz_id = name;
+                if(psz_id[0]=='#')
+                {
+                    psz_id++;
+                    pi_id = &i_id;
+                }
+                if(*psz_id)
+                {
+                    char *end = NULL;
+                    int i_val = std::strtol(psz_id, &end, 10);
+                    if(end != NULL && *end == '\0')
+                        *pi_id = i_val;
+                }
+                if(i_id != -1 || i_seqid != -1)
+                {
+                    msg_Dbg(obj,"found declaration for ES %s %d",
+                                (i_id > -1) ? "pid #" : "seq", *pi_id);
+                    int i_reserved_chans = 0;
+                    std::vector<uint8_t> subframeslots;
+                    for(config_chain_t *p = p_config_chain; p; p = p->p_next)
+                    {
+                        if(!std::strcmp("chans", p->psz_name) && subframeslots.empty())
+                        {
+                            char *end = NULL;
+                            int i_val = std::strtol(p->psz_value, &end, 10);
+                            if(end != NULL && *end == '\0')
+                            {
+                                i_reserved_chans = i_val;
+                                msg_Dbg(obj," * provisioned %d channels", i_val);
+                            }
+                            else msg_Warn(obj, " * ignoring channels count declaration %d", i_val);
+                        }
+                        else if(i_reserved_chans == 0)
+                        {
+                            char *end = NULL;
+                            int i_slot = std::strtol(p->psz_name, &end, 10);
+                            if(end != NULL && *end == '\0')
+                            {
+                                if(i_slot < MAX_AES3_AUDIO_SUBFRAMES && i_slot < (2 * framewidth) &&
+                                   std::find(subframeslots.begin(), subframeslots.end(), i_slot) == subframeslots.end())
+                                {
+                                    subframeslots.push_back(i_slot);
+                                    msg_Dbg(obj," * mapped channel %zd to subframe %d",
+                                                    subframeslots.size(), i_slot);
+                                }
+                                else msg_Warn(obj, " * ignoring invalid subframe declaration %d", i_slot);
+                            }
+                            else msg_Warn(obj, " * ignoring unknown/invalid token %s", p->psz_name);
+                        }
+                    }
+
+                    bool b_success = false;
+                    if(subframeslots.empty() && i_reserved_chans)
+                        b_success = addMapping(StreamID(i_id, i_seqid), i_reserved_chans);
+                    else if(!subframeslots.empty())
+                        b_success = addMapping(StreamID(i_id, i_seqid), subframeslots);
+
+                    if(b_success)
+                        msg_Dbg(obj, " * successfully configured");
+                    else
+                        msg_Warn(obj, " * configuration rejected (duplicate or not enough subframes ?)");
+                }
+            }
+            free(name);
+        }
+        config_ChainDestroy(p_config_chain);
+        if(psz != psz_in)
+            free(psz_in);
+        psz_in = psz_next;
+    }
+}
+
 std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
 {
     std::vector<uint8_t> slots;
@@ -97,6 +222,32 @@ std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
     return slots;
 }
 
+std::vector<uint8_t> SDIAudioMultiplexConfig::getConfiguredSlots(const StreamID &id) const
+{
+    for(size_t i=0; i<mappings.size(); i++)
+    {
+        if(mappings[i]->id == id)
+            return mappings[i]->subframesslots;
+    }
+    return std::vector<uint8_t>();
+}
+
+bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, const es_format_t *fmt)
+{
+    if(!fmt->audio.i_channels || !b_accept_any)
+        return false;
+    return addMapping(id, fmt->audio.i_channels);
+}
+
+bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, unsigned channels)
+{
+    std::vector<uint8_t> slots = getFreeSubFrameSlots();
+    if(slots.size() < channels)
+        return false;
+    slots.resize(channels);
+    return addMapping(id, slots);
+}
+
 bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t> subframeslots)
 {
     for(size_t i=0; i<mappings.size(); i++)
@@ -111,6 +262,9 @@ bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t
 
     mappings.push_back(assoc);
 
+    for(size_t i=0; i<subframeslots.size(); i++)
+        setSubFrameSlotUsed(subframeslots[i]);
+
     return true;
 }
 
@@ -130,6 +284,31 @@ SDIAudioMultiplexBuffer *
     return NULL;
 }
 
+const es_format_t * SDIAudioMultiplexConfig::getConfigurationForStream(const StreamID &id) const
+{
+    auto it = std::find_if(mappings.begin(), mappings.end(),
+                           [&id](Mapping *e) { return e->id == id; });
+    return (it != mappings.end()) ? &(*it)->fmt : NULL;
+}
+
+const es_format_t *
+    SDIAudioMultiplexConfig::updateFromRealESConfig(const StreamID &id,
+                                                    const es_format_t *fmt)
+{
+    auto it = std::find_if(mappings.begin(), mappings.end(),
+                           [&id](Mapping *e) { return e->id == id; });
+    if(it != mappings.end())
+    {
+        Mapping *mapping = (*it);
+        if(mapping->subframesslots.size() > 2 && fmt->audio.i_channels > 2)
+            ConfigureChannels(fmt->audio.i_channels, &mapping->fmt);
+        mapping->buffer.setSubFramesCount(mapping->fmt.audio.i_channels);
+        return &mapping->fmt;
+    }
+    assert(0);
+    return NULL;
+}
+
 SDIAudioMultiplex::SDIAudioMultiplex(uint8_t channels)
 {
     config = SDIAudioMultiplexConfig(channels);
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.hpp b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
index 7606b3d2e6..a571cf689f 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.hpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
@@ -46,26 +46,36 @@ namespace sdi_sout
             SDIAudioMultiplexConfig(uint8_t channels = 2);
             ~SDIAudioMultiplexConfig();
             SDIAudioMultiplexBuffer *getBufferForStream(const StreamID &);
+            const es_format_t * getConfigurationForStream(const StreamID &) const;
+            const es_format_t * updateFromRealESConfig(const StreamID &,
+                                                       const es_format_t *);
             bool SubFrameSlotUsed(uint8_t) const;
             void setSubFrameSlotUsed(uint8_t);
+            void parseConfiguration(vlc_object_t *, const char *);
             uint8_t getMultiplexedFramesCount() const { return framewidth; }
             std::vector<uint8_t> getFreeSubFrameSlots() const;
+            std::vector<uint8_t> getConfiguredSlots(const StreamID &) const;
 
+            bool addMapping(const StreamID &, const es_format_t *);
             bool addMapping(const StreamID &, std::vector<uint8_t>);
             unsigned getMaxSamplesForBlockSize(size_t) const;
 
         private:
+            bool addMapping(const StreamID &, unsigned);
             class Mapping
             {
                 public:
                     Mapping(const StreamID &);
+                    ~Mapping();
                     StreamID id;
+                    es_format_t fmt;
                     SDIAudioMultiplexBuffer buffer;
                     std::vector<uint8_t> subframesslots;
             };
             std::vector<Mapping *> mappings;
             unsigned subframeslotbitmap;
             uint8_t framewidth;
+            bool b_accept_any;
     };
 
     class SDIAudioMultiplex
diff --git a/modules/stream_out/sdi/SDIOutput.cpp b/modules/stream_out/sdi/SDIOutput.cpp
index fce8a00d77..f3ee7f7d7a 100644
--- a/modules/stream_out/sdi/SDIOutput.cpp
+++ b/modules/stream_out/sdi/SDIOutput.cpp
@@ -42,11 +42,11 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
     p_stream->pace_nocontrol = true;
 
     es_format_Init(&video.configuredfmt, VIDEO_ES, 0);
-    es_format_Init(&audio.configuredfmt, AUDIO_ES, 0);
     video.tenbits = var_InheritBool(p_stream, CFG_PREFIX "tenbits");
     video.nosignal_delay = var_InheritInteger(p_stream, CFG_PREFIX "nosignal-delay");
     video.pic_nosignal = NULL;
     audio.i_channels = var_InheritInteger(p_stream, CFG_PREFIX "channels");;
+    audio.b_configured = false;
     ancillary.afd = var_InheritInteger(p_stream, CFG_PREFIX "afd");
     ancillary.ar = var_InheritInteger(p_stream, CFG_PREFIX "ar");
     ancillary.afd_line = var_InheritInteger(p_stream, CFG_PREFIX "afd-line");
@@ -54,6 +54,12 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
     videoStream = NULL;
     captionsStream = NULL;
     audioMultiplex = new SDIAudioMultiplex( var_InheritInteger(p_stream, CFG_PREFIX "channels") );
+    char *psz_channelsconf = var_InheritString(p_stream, CFG_PREFIX "audio");
+    if(psz_channelsconf)
+    {
+        audioMultiplex->config.parseConfiguration(VLC_OBJECT(p_stream), psz_channelsconf);
+        free(psz_channelsconf);
+    }
 }
 
 SDIOutput::~SDIOutput()
@@ -69,7 +75,6 @@ SDIOutput::~SDIOutput()
     if(video.pic_nosignal)
         picture_Release(video.pic_nosignal);
     es_format_Clean(&video.configuredfmt);
-    es_format_Clean(&audio.configuredfmt);
 }
 
 AbstractStream *SDIOutput::Add(const es_format_t *fmt)
@@ -88,27 +93,29 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
     }
     else if(fmt->i_cat == AUDIO_ES && audio.i_channels)
     {
-        if(audio.configuredfmt.i_codec || ConfigureAudio(&fmt->audio) == VLC_SUCCESS)
+        if(audio.b_configured || ConfigureAudio(&fmt->audio) == VLC_SUCCESS)
         {
-            std::vector<uint8_t> slots = audioMultiplex->config.getFreeSubFrameSlots();
-            if(slots.size() < 2)
-                return NULL;
-            slots.resize(2);
-            if(!audioMultiplex->config.addMapping(id, slots))
-                return NULL;
+            const es_format_t *cfgfmt = audioMultiplex->config.getConfigurationForStream(id);
+            if(!cfgfmt)
+            {
+                if(!audioMultiplex->config.addMapping(id, fmt))
+                    return NULL;
+            }
+            cfgfmt = audioMultiplex->config.updateFromRealESConfig(id, fmt);
             SDIAudioMultiplexBuffer *buffer = audioMultiplex->config.getBufferForStream(id);
             if(!buffer)
                 return NULL;
-
             AudioDecodedStream *audioStream;
             s = audioStream = dynamic_cast<AudioDecodedStream *>(createStream(id, fmt, buffer));
             if(audioStream)
             {
-                audioStream->setOutputFormat(&audio.configuredfmt);
+                audioStream->setOutputFormat(cfgfmt);
                 audioStreams.push_back(audioStream);
+                std::vector<uint8_t> slots = audioMultiplex->config.getConfiguredSlots(id);
                 for(size_t i=0; i<slots.size(); i++)
                 {
-                    audioMultiplex->config.setSubFrameSlotUsed(slots[i]);
+                    msg_Dbg(p_stream, "%s slot %d to read from channel %zd",
+                                      id.toString().c_str(), slots[i], i);
                     audioMultiplex->SetSubFrameSource(slots[i], buffer, AES3AudioSubFrameIndex(i));
                 }
             }
diff --git a/modules/stream_out/sdi/SDIOutput.hpp b/modules/stream_out/sdi/SDIOutput.hpp
index c2fb2d77d4..290f2a315b 100644
--- a/modules/stream_out/sdi/SDIOutput.hpp
+++ b/modules/stream_out/sdi/SDIOutput.hpp
@@ -64,8 +64,8 @@ namespace sdi_sout
 
             struct
             {
-                es_format_t configuredfmt;
                 uint8_t i_channels;
+                bool b_configured;
             } audio;
 
             struct
diff --git a/modules/stream_out/sdi/sdiout.cpp b/modules/stream_out/sdi/sdiout.cpp
index eb450cfb10..c7b3f88200 100644
--- a/modules/stream_out/sdi/sdiout.cpp
+++ b/modules/stream_out/sdi/sdiout.cpp
@@ -75,6 +75,15 @@
 #define VIDEO_TENBITS_LONGTEXT N_(\
     "Use 10 bits per pixel for video frames.")
 
+#define AUDIO_TEXT "Audio channels configuration (Default, single, auto)"
+#define AUDIO_LONGTEXT "Configuration string SEL{CHANS} tokens, ':' separated. " \
+                       "SEL selectors being #145 for ES id 145, or 1 for second created ES. " \
+                       "CHANS being {n,n+1,..} channels to subframe mapping. " \
+                       "{chans=6} shortcut to request 6 channels in same order. " \
+                       "Use 'only' to accept only declared ES. " \
+                       "ex: only:#145{0,1}:#142{2,3}:2{chans=6} "
+
+
 /* Video Connections */
 static const char *const ppsz_videoconns[] = {
     "sdi",
@@ -201,5 +210,6 @@ vlc_module_begin ()
     set_section(N_("DeckLink Audio Options"), NULL)
     add_integer_with_range(CFG_PREFIX "channels", 2, 0, 16,
                 CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
+    add_string(CFG_PREFIX "audio", "", AUDIO_TEXT, AUDIO_LONGTEXT, true)
 
 vlc_module_end ()



More information about the vlc-commits mailing list