[vlc-commits] sout: sdi: multiplex multiple audio streams

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


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon Jul 23 17:04:25 2018 +0200| [10e59768e305016bb162a4446afb0e4495bb0911] | committer: Francois Cartegnie

sout: sdi: multiplex multiple audio streams

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

 modules/stream_out/Makefile.am               |   4 +
 modules/stream_out/sdi/AES3Audio.cpp         | 266 +++++++++++++++++++++++++++
 modules/stream_out/sdi/AES3Audio.hpp         | 105 +++++++++++
 modules/stream_out/sdi/DBMSDIOutput.cpp      |  40 ++--
 modules/stream_out/sdi/SDIAudioMultiplex.cpp | 235 +++++++++++++++++++++++
 modules/stream_out/sdi/SDIAudioMultiplex.hpp |  91 +++++++++
 modules/stream_out/sdi/SDIOutput.cpp         |  47 ++++-
 modules/stream_out/sdi/SDIOutput.hpp         |   7 +-
 modules/stream_out/sdi/SDIStream.cpp         |   8 +-
 modules/stream_out/sdi/SDIStream.hpp         |  16 +-
 10 files changed, 785 insertions(+), 34 deletions(-)

diff --git a/modules/stream_out/Makefile.am b/modules/stream_out/Makefile.am
index f27f3e066e..6bfc38fff2 100644
--- a/modules/stream_out/Makefile.am
+++ b/modules/stream_out/Makefile.am
@@ -57,8 +57,12 @@ libstream_out_sdi_plugin_la_SOURCES = stream_out/sdi/sdiout.cpp \
         stream_out/sdi/sdiout.hpp \
         stream_out/sdi/Ancillary.cpp \
         stream_out/sdi/Ancillary.hpp \
+        stream_out/sdi/AES3Audio.cpp \
+        stream_out/sdi/AES3Audio.hpp \
         stream_out/sdi/DBMSDIOutput.cpp \
         stream_out/sdi/DBMSDIOutput.hpp \
+        stream_out/sdi/SDIAudioMultiplex.cpp \
+        stream_out/sdi/SDIAudioMultiplex.hpp \
         stream_out/sdi/SDIOutput.cpp \
         stream_out/sdi/SDIOutput.hpp \
         stream_out/sdi/SDIStream.cpp \
diff --git a/modules/stream_out/sdi/AES3Audio.cpp b/modules/stream_out/sdi/AES3Audio.cpp
new file mode 100644
index 0000000000..fe06149008
--- /dev/null
+++ b/modules/stream_out/sdi/AES3Audio.cpp
@@ -0,0 +1,266 @@
+/*****************************************************************************
+ * AES3Buffer.cpp: AES3 audio buffer
+ *****************************************************************************
+ * Copyright © 2018 VideoLabs, VideoLAN and VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "AES3Audio.hpp"
+#include <algorithm>
+#include <cassert>
+
+using namespace sdi_sout;
+
+AES3AudioBuffer::AES3AudioBuffer(unsigned count)
+{
+    setSubFramesCount(count);
+    block_BytestreamInit(&bytestream);
+    toconsume = 0;
+}
+
+AES3AudioBuffer::~AES3AudioBuffer()
+{
+    block_BytestreamRelease(&bytestream);
+}
+
+void AES3AudioBuffer::setSubFramesCount(uint8_t c)
+{
+    buffersubframes = c;
+}
+
+void AES3AudioBuffer::push(block_t *p_block)
+{
+    bytestream_mutex.lock();
+    block_BytestreamPush(&bytestream, p_block);
+    bytestream_mutex.unlock();
+}
+
+void AES3AudioBuffer::read(void *dstbuf, unsigned count,
+                           const AES3AudioSubFrameIndex &dstbufsubframeidx,
+                           const AES3AudioSubFrameIndex &srcchannelidx,
+                           unsigned dstbufframeswidth)
+{
+    if(!srcchannelidx.isValid() || srcchannelidx.index() >= buffersubframes)
+        return;
+
+    if(dstbuf == NULL)
+        return;
+    bytestream_mutex.lock();
+    uint8_t *dst = reinterpret_cast<uint8_t *>(dstbuf);
+    for(unsigned i=0; i<count; i++)
+    {
+       size_t srcoffset = sizeof(uint16_t) * (i * buffersubframes + srcchannelidx.index());
+       size_t dstoffset = sizeof(uint16_t) * (i * 2 * dstbufframeswidth + dstbufsubframeidx.index());
+       block_PeekOffsetBytes(&bytestream, srcoffset, &dst[dstoffset], sizeof(uint16_t));
+    }
+    bytestream_mutex.unlock();
+}
+
+size_t AES3AudioBuffer::FramesToBytes(unsigned f) const
+{
+    return (size_t) f * sizeof(uint16_t) * buffersubframes;
+}
+
+int64_t AES3AudioBuffer::FramesToDuration(unsigned f) const
+{
+    return CLOCK_FREQ * f / 48000;
+}
+
+unsigned AES3AudioBuffer::BytesToFrames(size_t s) const
+{
+    return s / (sizeof(uint16_t) * buffersubframes);
+}
+
+unsigned AES3AudioBuffer::TicksDurationToFrames(int64_t t) const
+{
+    return t * 48000 / CLOCK_FREQ;
+}
+
+void AES3AudioBuffer::flushConsumed()
+{
+    if(toconsume)
+    {
+        size_t bytes = FramesToBytes(toconsume);
+        bytestream_mutex.lock();
+        block_SkipBytes(&bytestream, bytes);
+        block_BytestreamFlush(&bytestream);
+        bytestream_mutex.unlock();
+        toconsume = 0;
+    }
+}
+
+void AES3AudioBuffer::tagConsumed(unsigned f)
+{
+    assert(toconsume == 0 || toconsume == f);
+    toconsume = f;
+}
+
+void AES3AudioBuffer::forwardTo(vlc_tick_t t)
+{
+    if(bufferStart() == VLC_TICK_INVALID)
+        return;
+
+    tagConsumed(TicksDurationToFrames(t - bytestream.p_block->i_pts));
+}
+
+vlc_tick_t AES3AudioBuffer::bufferStart() const
+{
+    vlc_tick_t start = VLC_TICK_INVALID;
+    bytestream_mutex.lock();
+    if(bytestream.p_block)
+        start = bytestream.p_block->i_pts +
+                FramesToDuration(BytesToFrames(bytestream.i_block_offset));
+    bytestream_mutex.unlock();
+    return start;
+}
+
+vlc_tick_t AES3AudioBuffer::bufferEnd() const
+{
+    vlc_tick_t start = bufferStart();
+    if(start != VLC_TICK_INVALID)
+        start += CLOCK_FREQ * FramesToDuration(BytesToFrames(block_BytestreamRemaining(&bytestream)));
+     return start;
+}
+
+unsigned AES3AudioBuffer::availableSamples() const
+{
+    bytestream_mutex.lock();
+    unsigned samples = BytesToFrames(block_BytestreamRemaining(&bytestream));
+    bytestream_mutex.unlock();
+    return samples;
+}
+
+AES3AudioSubFrameSource::AES3AudioSubFrameSource()
+{
+    aes3AudioBuffer = NULL;
+}
+
+AES3AudioSubFrameSource::AES3AudioSubFrameSource(AES3AudioBuffer *buf, AES3AudioSubFrameIndex idx)
+{
+    aes3AudioBuffer = buf;
+    bufferSubFrameIdx = idx;
+}
+
+vlc_tick_t AES3AudioSubFrameSource::bufferStartTime() const
+{
+    if(available())
+        return VLC_TICK_INVALID;
+    else return aes3AudioBuffer->bufferStart();
+}
+
+void AES3AudioSubFrameSource::copy(void *buf,
+                                   unsigned count,
+                                   const AES3AudioSubFrameIndex &srcsubframeidx,
+                                   unsigned widthinframes)
+{
+    if(aes3AudioBuffer == NULL)
+        return;
+    aes3AudioBuffer->read(buf, count, srcsubframeidx, bufferSubFrameIdx, widthinframes);
+}
+
+void AES3AudioSubFrameSource::flushConsumed()
+{
+    if(aes3AudioBuffer)
+        aes3AudioBuffer->flushConsumed();
+}
+
+void AES3AudioSubFrameSource::tagConsumed(unsigned count)
+{
+    if(aes3AudioBuffer)
+        aes3AudioBuffer->tagConsumed(count);
+}
+
+const AES3AudioSubFrameIndex & AES3AudioSubFrameSource::index() const
+{
+    return bufferSubFrameIdx;
+}
+
+bool AES3AudioSubFrameSource::available() const
+{
+    return aes3AudioBuffer == NULL;
+}
+
+unsigned AES3AudioSubFrameSource::availableSamples() const
+{
+    if(aes3AudioBuffer == NULL)
+        return 0;
+    return aes3AudioBuffer->availableSamples();
+}
+
+AES3AudioFrameSource::AES3AudioFrameSource()
+{
+
+}
+
+vlc_tick_t AES3AudioFrameSource::bufferStartTime() const
+{
+    vlc_tick_t ret0 = subframe0.bufferStartTime();
+    vlc_tick_t ret1 = subframe1.bufferStartTime();
+    if(ret0 == VLC_TICK_INVALID)
+       return ret1;
+    else if(ret1 == VLC_TICK_INVALID || ret1 > ret0)
+       return ret0;
+    else
+       return ret1;
+}
+
+unsigned AES3AudioFrameSource::samplesUpToTime(vlc_tick_t t) const
+{
+    int64_t diff = t - bufferStartTime();
+    if(diff <= 0)
+        return 0;
+    return diff / (48000 * 2 * 2);
+}
+
+unsigned AES3AudioFrameSource::availableSamples() const
+{
+    if(!subframe0.available() && !subframe1.available())
+        return std::min(subframe0.availableSamples(), subframe1.availableSamples());
+    else if(subframe1.available())
+        return subframe0.availableSamples();
+    else
+        return subframe1.availableSamples();
+}
+
+void AES3AudioFrameSource::flushConsumed()
+{
+    subframe0.flushConsumed();
+    subframe1.flushConsumed();
+}
+
+void AES3AudioFrameSource::tagConsumed(unsigned samples)
+{
+    subframe0.tagConsumed(samples);
+    subframe1.tagConsumed(samples);
+}
+
+AES3AudioSubFrameIndex::AES3AudioSubFrameIndex(uint8_t v)
+{
+    subframeindex = v;
+}
+
+uint8_t AES3AudioSubFrameIndex::index() const
+{
+    return subframeindex;
+}
+
+bool AES3AudioSubFrameIndex::isValid() const
+{
+    return subframeindex < MAX_AES3_AUDIO_SUBFRAMES;
+}
diff --git a/modules/stream_out/sdi/AES3Audio.hpp b/modules/stream_out/sdi/AES3Audio.hpp
new file mode 100644
index 0000000000..09abe095f6
--- /dev/null
+++ b/modules/stream_out/sdi/AES3Audio.hpp
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ * AES3Buffer.hpp: AES3 audio buffer
+ *****************************************************************************
+ * Copyright © 2018 VideoLabs, VideoLAN and VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef AES3AUDIO_HPP
+#define AES3AUDIO_HPP
+
+#include <vlc_common.h>
+#include <vlc_block.h>
+#include <vlc_block_helper.h>
+#include <mutex>
+
+#define MAX_AES3_AUDIO_FRAMES     8
+#define MAX_AES3_AUDIO_SUBFRAMES (MAX_AES3_AUDIO_FRAMES * 2)
+
+namespace sdi_sout
+{
+    class AES3AudioSubFrameIndex
+    {
+        public:
+            AES3AudioSubFrameIndex(uint8_t = MAX_AES3_AUDIO_SUBFRAMES);
+            uint8_t index() const;
+            bool isValid() const;
+        private:
+            uint8_t subframeindex;
+    };
+
+    class AES3AudioBuffer
+    {
+        public:
+            AES3AudioBuffer(unsigned = 0);
+            ~AES3AudioBuffer();
+            void setSubFramesCount(uint8_t);
+            vlc_tick_t bufferStart() const;
+            vlc_tick_t bufferEnd() const;
+            unsigned availableSamples() const;
+            void push(block_t *);
+            void read(void *, unsigned,
+                      const AES3AudioSubFrameIndex &,
+                      const AES3AudioSubFrameIndex &, unsigned);
+            void flushConsumed();
+            void tagConsumed(unsigned);
+            void forwardTo(vlc_tick_t);
+
+        private:
+            size_t   FramesToBytes(unsigned) const;
+            int64_t  FramesToDuration(unsigned) const;
+            unsigned BytesToFrames(size_t) const;
+            unsigned TicksDurationToFrames(int64_t) const;
+            block_bytestream_t bytestream;
+            mutable std::mutex bytestream_mutex;
+            uint8_t buffersubframes;
+            unsigned toconsume;
+    };
+
+    class AES3AudioSubFrameSource
+    {
+        public:
+            AES3AudioSubFrameSource();
+            AES3AudioSubFrameSource(AES3AudioBuffer *, AES3AudioSubFrameIndex);
+            vlc_tick_t bufferStartTime() const;
+            void copy(void *, unsigned count,
+                      const AES3AudioSubFrameIndex &, unsigned width);
+            void flushConsumed();
+            void tagConsumed(unsigned);
+            unsigned availableSamples() const;
+            const AES3AudioSubFrameIndex & index() const;
+            bool available() const;
+
+        private:
+            AES3AudioBuffer *aes3AudioBuffer;
+            AES3AudioSubFrameIndex bufferSubFrameIdx; /* alias channel */
+    };
+
+    class AES3AudioFrameSource
+    {
+        public:
+            AES3AudioFrameSource();
+            vlc_tick_t bufferStartTime() const;
+            unsigned samplesUpToTime(vlc_tick_t) const;
+            unsigned availableSamples() const;
+            void flushConsumed();
+            void tagConsumed(unsigned);
+            AES3AudioSubFrameSource subframe0;
+            AES3AudioSubFrameSource subframe1;
+    };
+
+}
+
+#endif // AES3AUDIO_HPP
diff --git a/modules/stream_out/sdi/DBMSDIOutput.cpp b/modules/stream_out/sdi/DBMSDIOutput.cpp
index cf56f92e2a..d2e602fd36 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.cpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.cpp
@@ -24,6 +24,7 @@
 
 #include "DBMSDIOutput.hpp"
 #include "SDIStream.hpp"
+#include "SDIAudioMultiplex.hpp"
 #include "Ancillary.hpp"
 #include "V210.hpp"
 
@@ -50,6 +51,10 @@ DBMSDIOutput::DBMSDIOutput(sout_stream_t *p_stream) :
 
 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;
@@ -69,7 +74,7 @@ AbstractStream *DBMSDIOutput::Add(const es_format_t *fmt)
     {
         msg_Dbg(p_stream, "accepted %s %4.4s",
                           s->getID().toString().c_str(), (const char *) &fmt->i_codec);
-        if( videoStream && (audioStream || audio.i_channels == 0) )
+        if( videoStream && (!audioStreams.empty() || audio.i_channels == 0) )
             Start();
     }
     else
@@ -269,6 +274,15 @@ 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;
 
@@ -280,19 +294,12 @@ int DBMSDIOutput::ConfigureAudio(const audio_format_t *)
 
     if (audio.i_channels > 0)
     {
-        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 = FRAME_SIZE;
-
+        uint8_t maxchannels = audioMultiplex->config.getMultiplexedFramesCount() * 2;
+        msg_Dbg(p_stream, "configuring audio output with %d", maxchannels);
         result = p_output->EnableAudioOutput(
                     bmdAudioSampleRate48kHz,
                     bmdAudioSampleType16bitInteger,
-                    2,
+                    maxchannels,
                     bmdAudioOutputStreamTimestamped);
         CHECK("Could not start audio output");
     }
@@ -507,9 +514,14 @@ int DBMSDIOutput::Process()
     while((p = reinterpret_cast<picture_t *>(videoBuffer.Dequeue())))
         ProcessVideo(p, reinterpret_cast<block_t *>(captionsBuffer.Dequeue()));
 
-    block_t *b;
-    while((b = reinterpret_cast<block_t *>(audioBuffer.Dequeue())))
-        ProcessAudio(b);
+    while(audioMultiplex->availableSamples() >= SAMPLES_PER_FRAME)
+    {
+          block_t *out = audioMultiplex->Extract(SAMPLES_PER_FRAME);
+          if(out)
+          {
+              ProcessAudio(out);
+          }
+    }
 
     return VLC_SUCCESS;
 }
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.cpp b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
new file mode 100644
index 0000000000..35993947c5
--- /dev/null
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
@@ -0,0 +1,235 @@
+/*****************************************************************************
+ * SDIAudioMultiplex.cpp: SDI Audio Multiplexing
+ *****************************************************************************
+ * Copyright © 2018 VideoLabs, VideoLAN and VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "SDIAudioMultiplex.hpp"
+#include <limits>
+
+using namespace sdi_sout;
+
+SDIAudioMultiplexBuffer::SDIAudioMultiplexBuffer()
+    : AES3AudioBuffer(2), AbstractStreamOutputBuffer()
+{
+
+}
+
+SDIAudioMultiplexBuffer::~SDIAudioMultiplexBuffer()
+{
+    FlushQueued();
+}
+
+void SDIAudioMultiplexBuffer::FlushQueued()
+{
+
+}
+
+void SDIAudioMultiplexBuffer::Enqueue(void *p)
+{
+    AES3AudioBuffer::push(reinterpret_cast<block_t *>(p));
+}
+
+void * SDIAudioMultiplexBuffer::Dequeue()
+{
+    return NULL;
+}
+
+SDIAudioMultiplexConfig::Mapping::Mapping(const StreamID &id)
+    : id(id)
+{
+
+}
+
+SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(uint8_t channels)
+{
+    subframeslotbitmap = 0;
+    if(channels > 4)
+        framewidth = 8;
+    else if(channels > 2)
+        framewidth = 4;
+    else
+        framewidth = 1;
+}
+
+SDIAudioMultiplexConfig::~SDIAudioMultiplexConfig()
+{
+    for(size_t i=0; i<mappings.size(); i++)
+        delete mappings[i];
+}
+
+bool SDIAudioMultiplexConfig::SubFrameSlotUsed(uint8_t i) const
+{
+    return (1 << i) & subframeslotbitmap;
+}
+
+void SDIAudioMultiplexConfig::setSubFrameSlotUsed(uint8_t i)
+{
+    subframeslotbitmap |= (1 << i);
+}
+
+std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
+{
+    std::vector<uint8_t> slots;
+    for(uint8_t i=0; i<getMultiplexedFramesCount() * 2; i++)
+    {
+        if(!SubFrameSlotUsed(i))
+            slots.push_back(i);
+    }
+
+    return slots;
+}
+
+bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t> subframeslots)
+{
+    for(size_t i=0; i<mappings.size(); i++)
+        if(mappings[i]->id == id)
+            return false;
+    for(size_t i=0; i<subframeslots.size(); i++)
+        if(SubFrameSlotUsed(subframeslots[i]))
+            return false;
+
+    Mapping *assoc = new Mapping(id);
+    assoc->subframesslots = subframeslots;
+
+    mappings.push_back(assoc);
+
+    return true;
+}
+
+unsigned SDIAudioMultiplexConfig::getMaxSamplesForBlockSize(size_t s) const
+{
+    return s / (2 * sizeof(uint16_t) * getMultiplexedFramesCount());
+}
+
+SDIAudioMultiplexBuffer *
+    SDIAudioMultiplexConfig::getBufferForStream(const StreamID &id)
+{
+    for(size_t i=0; i<mappings.size(); i++)
+    {
+        if(mappings[i]->id == id)
+            return &mappings[i]->buffer;
+    }
+    return NULL;
+}
+
+SDIAudioMultiplex::SDIAudioMultiplex(uint8_t channels)
+{
+    config = SDIAudioMultiplexConfig(channels);
+}
+
+SDIAudioMultiplex::~SDIAudioMultiplex()
+{
+
+}
+
+unsigned SDIAudioMultiplex::availableSamples() const
+{
+    unsigned samples = std::numeric_limits<unsigned>::max();
+    for(size_t i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+    {
+        if(framesources[i].subframe0.available() &&
+           framesources[i].subframe1.available())
+            continue;
+        samples = std::min(samples, framesources[i].availableSamples());
+    }
+    return samples < std::numeric_limits<unsigned>::max() ? samples : 0;
+}
+
+vlc_tick_t SDIAudioMultiplex::bufferStart() const
+{
+    vlc_tick_t start = VLC_TICK_INVALID;
+    for(size_t i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+    {
+        if(framesources[i].subframe0.available() &&
+           framesources[i].subframe1.available())
+            continue;
+        vlc_tick_t t = framesources[i].bufferStartTime();
+        if(start == VLC_TICK_INVALID ||
+           (t != VLC_TICK_INVALID && t<start))
+            start = t;
+    }
+    return start;
+}
+
+unsigned SDIAudioMultiplex::getFreeSubFrameSlots() const
+{
+    unsigned bitfield = 0;
+    for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+    {
+        const AES3AudioFrameSource *source = &framesources[i];
+        if(source->subframe0.available())
+            bitfield |= (1 << (i * 2 + 0));
+        if(source->subframe1.available())
+            bitfield |= (1 << (i * 2 + 1));
+    }
+    return bitfield;
+}
+
+void SDIAudioMultiplex::SetSubFrameSource(uint8_t n, AES3AudioBuffer *buf,
+                                          AES3AudioSubFrameIndex idx)
+{
+    assert(n<MAX_AES3_AUDIO_SUBFRAMES);
+    AES3AudioFrameSource *f = &framesources[n / 2];
+    AES3AudioSubFrameSource *s = (n & 1) ? &f->subframe1 : &f->subframe0;
+    assert(s->available());
+    *s = AES3AudioSubFrameSource(buf, idx);
+}
+
+block_t * SDIAudioMultiplex::Extract(unsigned samples)
+{
+    vlc_tick_t start = bufferStart();
+
+    uint8_t interleavedframes = config.getMultiplexedFramesCount();
+
+    block_t *p_block = block_Alloc( interleavedframes * 2 * sizeof(uint16_t) * samples );
+    if(!p_block)
+        return NULL;
+    memset(p_block->p_buffer, 0, p_block->i_buffer);
+
+    p_block->i_pts = p_block->i_dts = start;
+    p_block->i_nb_samples = samples;
+
+    for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+    {
+        AES3AudioFrameSource *source = &framesources[i];
+        unsigned avail = source->availableSamples();
+        if(avail == 0)
+            continue;
+
+        unsigned toskip = 0;
+        unsigned tocopy = std::min(samples, avail);
+
+        toskip = source->samplesUpToTime(start);
+        if(toskip > tocopy)
+            continue;
+        tocopy -= toskip;
+
+        source->subframe0.copy(p_block->p_buffer, tocopy, (i * 2 + 0), interleavedframes);
+        source->subframe1.copy(p_block->p_buffer, tocopy, (i * 2 + 1), interleavedframes);
+    }
+
+    for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+        framesources[i].tagConsumed(samples);
+    for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+        framesources[i].flushConsumed();
+
+    return p_block;
+}
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.hpp b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
new file mode 100644
index 0000000000..7606b3d2e6
--- /dev/null
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * SDIAudioMultiplex.hpp: SDI Audio Multiplexing
+ *****************************************************************************
+ * Copyright © 2018 VideoLabs, VideoLAN and VideoLAN Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef SDIAUDIOMULTIPLEX_HPP
+#define SDIAUDIOMULTIPLEX_HPP
+
+#include "AES3Audio.hpp"
+#include "SDIStream.hpp"
+
+#include <vector>
+
+#define SAMPLES_PER_FRAME        (1536/4)
+
+namespace sdi_sout
+{
+    class SDIAudioMultiplexBuffer : public AES3AudioBuffer,
+                                    public AbstractStreamOutputBuffer
+    {
+        public:
+            SDIAudioMultiplexBuffer();
+            virtual ~SDIAudioMultiplexBuffer();
+            virtual void FlushQueued(); /* impl */
+            virtual void Enqueue(void *); /* impl */
+            virtual void * Dequeue(); /* impl */
+    };
+
+    class SDIAudioMultiplexConfig
+    {
+        public:
+            SDIAudioMultiplexConfig(uint8_t channels = 2);
+            ~SDIAudioMultiplexConfig();
+            SDIAudioMultiplexBuffer *getBufferForStream(const StreamID &);
+            bool SubFrameSlotUsed(uint8_t) const;
+            void setSubFrameSlotUsed(uint8_t);
+            uint8_t getMultiplexedFramesCount() const { return framewidth; }
+            std::vector<uint8_t> getFreeSubFrameSlots() const;
+
+            bool addMapping(const StreamID &, std::vector<uint8_t>);
+            unsigned getMaxSamplesForBlockSize(size_t) const;
+
+        private:
+            class Mapping
+            {
+                public:
+                    Mapping(const StreamID &);
+                    StreamID id;
+                    SDIAudioMultiplexBuffer buffer;
+                    std::vector<uint8_t> subframesslots;
+            };
+            std::vector<Mapping *> mappings;
+            unsigned subframeslotbitmap;
+            uint8_t framewidth;
+    };
+
+    class SDIAudioMultiplex
+    {
+        public:
+            SDIAudioMultiplex(uint8_t channels);
+            ~SDIAudioMultiplex();
+            vlc_tick_t bufferStart() const;
+            unsigned availableSamples() const;
+            block_t * Extract(unsigned);
+            unsigned getFreeSubFrameSlots() const;
+            void SetSubFrameSource(uint8_t, AES3AudioBuffer *, AES3AudioSubFrameIndex);
+
+            SDIAudioMultiplexConfig config;
+
+        private:
+            unsigned count;
+            AES3AudioFrameSource framesources[MAX_AES3_AUDIO_FRAMES];
+    };
+}
+
+
+#endif // SDIAUDIOMULTIPLEX_HPP
diff --git a/modules/stream_out/sdi/SDIOutput.cpp b/modules/stream_out/sdi/SDIOutput.cpp
index 0048990077..fce8a00d77 100644
--- a/modules/stream_out/sdi/SDIOutput.cpp
+++ b/modules/stream_out/sdi/SDIOutput.cpp
@@ -23,6 +23,7 @@
 
 #include "SDIOutput.hpp"
 #include "SDIStream.hpp"
+#include "SDIAudioMultiplex.hpp"
 #include "sdiout.hpp"
 
 #include <vlc_sout.h>
@@ -51,15 +52,20 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
     ancillary.afd_line = var_InheritInteger(p_stream, CFG_PREFIX "afd-line");
     ancillary.captions_line = 15;
     videoStream = NULL;
-    audioStream = NULL;
     captionsStream = NULL;
+    audioMultiplex = new SDIAudioMultiplex( var_InheritInteger(p_stream, CFG_PREFIX "channels") );
 }
 
 SDIOutput::~SDIOutput()
 {
     videoBuffer.FlushQueued();
-    audioBuffer.FlushQueued();
     captionsBuffer.FlushQueued();
+    while(!audioStreams.empty())
+    {
+        delete audioStreams.front();
+        audioStreams.pop_front();
+    }
+    delete audioMultiplex;
     if(video.pic_nosignal)
         picture_Release(video.pic_nosignal);
     es_format_Clean(&video.configuredfmt);
@@ -80,12 +86,33 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
             videoStream->setCaptionsOutputBuffer(&captionsBuffer);
         }
     }
-    else if(fmt->i_cat == AUDIO_ES && audio.i_channels && !audioStream)
+    else if(fmt->i_cat == AUDIO_ES && audio.i_channels)
     {
-        if(ConfigureAudio(&fmt->audio) == VLC_SUCCESS)
-            s = audioStream = dynamic_cast<AudioDecodedStream *>(createStream(id, fmt, &audioBuffer));
-        if(audioStream)
-            audioStream->setOutputFormat(&audio.configuredfmt);
+        if(audio.configuredfmt.i_codec || 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;
+            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);
+                audioStreams.push_back(audioStream);
+                for(size_t i=0; i<slots.size(); i++)
+                {
+                    audioMultiplex->config.setSubFrameSlotUsed(slots[i]);
+                    audioMultiplex->SetSubFrameSource(slots[i], buffer, AES3AudioSubFrameIndex(i));
+                }
+            }
+        }
     }
     else if(fmt->i_cat == SPU_ES && !captionsStream)
     {
@@ -107,8 +134,10 @@ void SDIOutput::Del(AbstractStream *s)
     Process();
     if(videoStream == s)
         videoStream = NULL;
-    else if(audioStream == s)
-        audioStream = NULL;
+    else if(dynamic_cast<AudioDecodedStream *>(s))
+    {
+        audioStreams.remove(static_cast<AudioDecodedStream *>(s));
+    }
     else if(captionsStream == s)
         captionsStream = NULL;
     delete s;
diff --git a/modules/stream_out/sdi/SDIOutput.hpp b/modules/stream_out/sdi/SDIOutput.hpp
index f6836f5a93..c2fb2d77d4 100644
--- a/modules/stream_out/sdi/SDIOutput.hpp
+++ b/modules/stream_out/sdi/SDIOutput.hpp
@@ -22,9 +22,12 @@
 
 #include "SDIStream.hpp"
 #include <vlc_common.h>
+#include <list>
 
 namespace sdi_sout
 {
+    class SDIAudioMultiplex;
+
     class SDIOutput
     {
         public:
@@ -45,11 +48,11 @@ namespace sdi_sout
             virtual int ConfigureAudio(const audio_format_t *) = 0;
             sout_stream_t *p_stream;
             VideoDecodedStream *videoStream;
-            AudioDecodedStream *audioStream;
+            std::list<AudioDecodedStream *> audioStreams;
             CaptionsStream *captionsStream;
             PictureStreamOutputBuffer videoBuffer;
-            BlockStreamOutputBuffer audioBuffer;
             BlockStreamOutputBuffer captionsBuffer;
+            SDIAudioMultiplex *audioMultiplex;
 
             struct
             {
diff --git a/modules/stream_out/sdi/SDIStream.cpp b/modules/stream_out/sdi/SDIStream.cpp
index 892f4c1dc4..25110a4f2b 100644
--- a/modules/stream_out/sdi/SDIStream.cpp
+++ b/modules/stream_out/sdi/SDIStream.cpp
@@ -40,14 +40,14 @@ AbstractStreamOutputBuffer::~AbstractStreamOutputBuffer()
 {
 }
 
-void AbstractStreamOutputBuffer::Enqueue(void *p)
+void AbstractQueueStreamOutputBuffer::Enqueue(void *p)
 {
     queue_mutex.lock();
     queued.push(p);
     queue_mutex.unlock();
 }
 
-void *AbstractStreamOutputBuffer::Dequeue()
+void *AbstractQueueStreamOutputBuffer::Dequeue()
 {
     void *p = NULL;
     queue_mutex.lock();
@@ -61,7 +61,7 @@ void *AbstractStreamOutputBuffer::Dequeue()
 }
 
 BlockStreamOutputBuffer::BlockStreamOutputBuffer()
-    : AbstractStreamOutputBuffer()
+    : AbstractQueueStreamOutputBuffer()
 {
 
 }
@@ -80,7 +80,7 @@ void BlockStreamOutputBuffer::FlushQueued()
 
 
 PictureStreamOutputBuffer::PictureStreamOutputBuffer()
-    : AbstractStreamOutputBuffer()
+    : AbstractQueueStreamOutputBuffer()
 {
 
 }
diff --git a/modules/stream_out/sdi/SDIStream.hpp b/modules/stream_out/sdi/SDIStream.hpp
index 6618cd9b20..a09697fca0 100644
--- a/modules/stream_out/sdi/SDIStream.hpp
+++ b/modules/stream_out/sdi/SDIStream.hpp
@@ -35,15 +35,22 @@ namespace sdi_sout
             AbstractStreamOutputBuffer();
             virtual ~AbstractStreamOutputBuffer();
             virtual void FlushQueued() = 0;
-            void Enqueue(void *);
-            void * Dequeue();
+            virtual void Enqueue(void *) = 0;
+            virtual void * Dequeue() = 0;
+    };
+
+    class AbstractQueueStreamOutputBuffer : public AbstractStreamOutputBuffer
+    {
+        public:
+            virtual void Enqueue(void *);
+            virtual void * Dequeue();
 
         private:
             std::mutex queue_mutex;
             std::queue<void *> queued;
     };
 
-    class BlockStreamOutputBuffer : public AbstractStreamOutputBuffer
+    class BlockStreamOutputBuffer : public AbstractQueueStreamOutputBuffer
     {
         public:
             BlockStreamOutputBuffer();
@@ -51,7 +58,7 @@ namespace sdi_sout
             virtual void FlushQueued();
     };
 
-    class PictureStreamOutputBuffer : public AbstractStreamOutputBuffer
+    class PictureStreamOutputBuffer : public AbstractQueueStreamOutputBuffer
     {
         public:
             PictureStreamOutputBuffer();
@@ -134,7 +141,6 @@ namespace sdi_sout
             AbstractStreamOutputBuffer *captionsOutputBuffer;
     };
 
-#   define FRAME_SIZE 1920
     class AudioDecodedStream : public AbstractDecodedStream
     {
         public:



More information about the vlc-commits mailing list