[vlc-commits] sout: sdi: add support for AC3 passthrough

Francois Cartegnie git at videolan.org
Mon Oct 8 18:11:35 CEST 2018


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Wed Sep  5 20:45:19 2018 +0200| [22d73cbc75b5979a3441cf24c5fbc6cf085d717c] | committer: Francois Cartegnie

sout: sdi: add support for AC3 passthrough

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

 modules/stream_out/sdi/AES3Audio.cpp         | 222 +++++++++++++++++++++++----
 modules/stream_out/sdi/AES3Audio.hpp         |  35 +++--
 modules/stream_out/sdi/DBMSDIOutput.cpp      |  19 ++-
 modules/stream_out/sdi/SDIAudioMultiplex.cpp | 116 ++++++++++----
 modules/stream_out/sdi/SDIAudioMultiplex.hpp |  23 ++-
 modules/stream_out/sdi/SDIOutput.cpp         |  42 +++--
 modules/stream_out/sdi/SDIOutput.hpp         |   5 +-
 modules/stream_out/sdi/SDIStream.cpp         |  33 ++++
 modules/stream_out/sdi/SDIStream.hpp         |  10 ++
 modules/stream_out/sdi/sdiout.cpp            |   1 +
 modules/stream_out/sdi/sdiout.hpp            |   2 +
 11 files changed, 409 insertions(+), 99 deletions(-)

diff --git a/modules/stream_out/sdi/AES3Audio.cpp b/modules/stream_out/sdi/AES3Audio.cpp
index 1db8b671bb..32e1a8e0ed 100644
--- a/modules/stream_out/sdi/AES3Audio.cpp
+++ b/modules/stream_out/sdi/AES3Audio.cpp
@@ -21,17 +21,20 @@
 # include "config.h"
 #endif
 
+#include "sdiout.hpp"
 #include "AES3Audio.hpp"
 #include <algorithm>
 #include <cassert>
 
 using namespace sdi_sout;
 
-AES3AudioBuffer::AES3AudioBuffer(unsigned count)
+AES3AudioBuffer::AES3AudioBuffer(vlc_object_t *p_obj, unsigned count)
 {
+    obj = p_obj;
     setSubFramesCount(count);
     block_BytestreamInit(&bytestream);
     toconsume = 0;
+    i_codec = VLC_CODEC_S16N;
 }
 
 AES3AudioBuffer::~AES3AudioBuffer()
@@ -51,25 +54,62 @@ void AES3AudioBuffer::push(block_t *p_block)
     bytestream_mutex.unlock();
 }
 
-void AES3AudioBuffer::read(void *dstbuf, unsigned count, unsigned skip,
-                           const AES3AudioSubFrameIndex &dstbufsubframeidx,
-                           const AES3AudioSubFrameIndex &srcchannelidx,
-                           unsigned dstbufframeswidth)
+unsigned AES3AudioBuffer::read(void *dstbuf, unsigned count, vlc_tick_t from,
+                               const AES3AudioSubFrameIndex &dstbufsubframeidx,
+                               const AES3AudioSubFrameIndex &srcchannelidx,
+                               unsigned dstbufframeswidth)
 {
     if(!srcchannelidx.isValid() || srcchannelidx.index() >= buffersubframes)
-        return;
+        return 0;
+
+#ifdef SDI_MULTIPLEX_DEBUG
+    unsigned orig = count;
+    assert(count);
+#endif
+
+    unsigned dstpad = 0;
+    unsigned skip = 0;
+    int offset = OffsetToBufferStart(from);
+    if(llabs(offset) >= count)
+        return 0;
+
+    if(offset > 0) /* buffer is ahead in time */
+    {
+        dstpad = offset;
+        count -= offset;
+    }
+    else if(offset < 0)  /* we're past buffer start */
+    {
+        skip = -offset;
+        count += offset;
+    }
+
+#ifdef SDI_MULTIPLEX_DEBUG
+    unsigned inbuffer = BytesToFrames(block_BytestreamRemaining(&bytestream));
+    msg_Dbg(obj, "%4.4s inbuffer %u count %u/%u skip %u pad %u",
+            reinterpret_cast<const char *>(&i_codec), inbuffer, count, orig, skip, dstpad);
+    assert(count + skip <= inbuffer);
+    assert(count + dstpad <= orig);
+#endif
 
-    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 + skip) * buffersubframes + srcchannelidx.index());
-       size_t dstoffset = sizeof(uint16_t) * (i * 2 * dstbufframeswidth + dstbufsubframeidx.index());
-       block_PeekOffsetBytes(&bytestream, srcoffset, &dst[dstoffset], sizeof(uint16_t));
+       size_t dstoffset = sizeof(uint16_t) * ((i + dstpad) * 2 * dstbufframeswidth + dstbufsubframeidx.index());
+       if(i_codec != VLC_CODEC_S16N)
+       {
+           assert(bytestream.i_block_offset == 0 || skip == 0);
+           assert(bytestream.p_block->i_buffer < 4 ||
+                  GetWBE(&bytestream.p_block->p_buffer[4]) == 0xf872);
+       }
+       if(dst)
+            block_PeekOffsetBytes(&bytestream, srcoffset, &dst[dstoffset], sizeof(uint16_t));
     }
     bytestream_mutex.unlock();
+
+    return 0;
 }
 
 size_t AES3AudioBuffer::FramesToBytes(unsigned f) const
@@ -92,6 +132,18 @@ unsigned AES3AudioBuffer::TicksDurationToFrames(vlc_tick_t t) const
     return samples_from_vlc_tick(t, 48000);
 }
 
+int AES3AudioBuffer::OffsetToBufferStart(vlc_tick_t t) const
+{
+    vlc_tick_t bufferstart = bufferStart();
+    if(bufferstart == VLC_TICK_INVALID)
+        return 0;
+
+    if(t >= bufferstart)
+        return -TicksDurationToFrames(t - bufferstart);
+    else
+        return TicksDurationToFrames(bufferstart - t);
+}
+
 void AES3AudioBuffer::flushConsumed()
 {
     if(toconsume)
@@ -103,10 +155,42 @@ void AES3AudioBuffer::flushConsumed()
         else
             block_BytestreamEmpty(&bytestream);
         bytestream_mutex.unlock();
+#ifdef SDI_MULTIPLEX_DEBUG
+        msg_Dbg(obj, "%4.4s flushed off %zd -> pts %ld",
+                reinterpret_cast<const char *>(&i_codec),
+                bytestream.i_block_offset, bufferStart());
+#endif
         toconsume = 0;
     }
 }
 
+void AES3AudioBuffer::tagVirtualConsumed(vlc_tick_t from, unsigned f)
+{
+    if(bufferStart() == VLC_TICK_INVALID)
+    {
+        f = 0;
+    }
+    else
+    {
+        int offset = OffsetToBufferStart(from);
+        if(offset > 0)
+        {
+            if((unsigned)offset >= f)
+                f = 0;
+            else
+                f -= offset;
+        }
+        else if (offset < 0)
+        {
+            if((unsigned)(-offset) > f)
+                f = 0;
+            else
+                f += offset;
+        }
+    }
+    tagConsumed(f);
+}
+
 void AES3AudioBuffer::tagConsumed(unsigned f)
 {
     assert(toconsume == 0 || toconsume == f);
@@ -118,10 +202,20 @@ void AES3AudioBuffer::forwardTo(vlc_tick_t t)
     if(bufferStart() == VLC_TICK_INVALID || t <= bufferStart())
         return;
 
-    tagConsumed(TicksDurationToFrames(t - bytestream.p_block->i_pts));
+    tagConsumed(TicksDurationToFrames(t - bufferStart()));
     flushConsumed();
 }
 
+void AES3AudioBuffer::setCodec(vlc_fourcc_t i_codec)
+{
+    this->i_codec = i_codec;
+}
+
+vlc_fourcc_t AES3AudioBuffer::getCodec() const
+{
+    return i_codec;
+}
+
 vlc_tick_t AES3AudioBuffer::bufferStart() const
 {
     vlc_tick_t start = VLC_TICK_INVALID;
@@ -141,16 +235,56 @@ vlc_tick_t AES3AudioBuffer::bufferEnd() const
      return start;
 }
 
-unsigned AES3AudioBuffer::availableSamples(vlc_tick_t from) const
+unsigned AES3AudioBuffer::availableVirtualSamples(vlc_tick_t from) const
 {
     vlc_tick_t start = bufferStart();
     if(start == VLC_TICK_INVALID)
         return 0;
+
     bytestream_mutex.lock();
+    /* FIXME */
     unsigned samples = BytesToFrames(block_BytestreamRemaining(&bytestream));
     bytestream_mutex.unlock();
-    unsigned offset = TicksDurationToFrames(from - start);
-    return samples + offset;
+
+    int offset = OffsetToBufferStart(from);
+    if(offset > 0)
+    {
+        samples += offset;
+    }
+    else if(offset < 0)
+    {
+        if((unsigned)-offset > samples)
+            samples = 0;
+        else
+            samples += offset;
+    }
+
+    return samples;
+}
+
+unsigned AES3AudioBuffer::alignedInterleaveInSamples(vlc_tick_t from, unsigned i_wanted) const
+{
+    if(i_codec == VLC_CODEC_S16N)
+        return i_wanted;
+    if(!bytestream.p_block)
+        return i_wanted; /* no care, won't be able to read */
+    unsigned samples = BytesToFrames(bytestream.p_block->i_buffer - bytestream.i_block_offset);
+    int offsetsamples = OffsetToBufferStart(from);
+    if(offsetsamples > 0)
+    {
+        /* align to our start */
+        samples = offsetsamples;
+    }
+    else if(offsetsamples < 0)
+    {
+        /* align to our end */
+    }
+#ifdef SDI_MULTIPLEX_DEBUG
+    msg_Dbg(obj, "%4.4s interleave samples %u -- ibuf %zd off %zd",
+            reinterpret_cast<const char *>(&i_codec), samples,
+            bytestream.p_block->i_buffer, bytestream.i_block_offset);
+#endif
+    return samples;
 }
 
 AES3AudioSubFrameSource::AES3AudioSubFrameSource()
@@ -171,15 +305,15 @@ vlc_tick_t AES3AudioSubFrameSource::bufferStartTime() const
     else return aes3AudioBuffer->bufferStart();
 }
 
-void AES3AudioSubFrameSource::copy(void *buf,
-                                   unsigned count,
-                                   unsigned skip,
-                                   const AES3AudioSubFrameIndex &srcsubframeidx,
-                                   unsigned widthinframes)
+unsigned AES3AudioSubFrameSource::copy(void *buf,
+                                       unsigned count,
+                                       vlc_tick_t from,
+                                       const AES3AudioSubFrameIndex &srcsubframeidx,
+                                       unsigned widthinframes)
 {
     if(aes3AudioBuffer == NULL)
-        return;
-    aes3AudioBuffer->read(buf, count, skip, srcsubframeidx, bufferSubFrameIdx, widthinframes);
+        return 0;
+    return aes3AudioBuffer->read(buf, count, from, srcsubframeidx, bufferSubFrameIdx, widthinframes);
 }
 
 void AES3AudioSubFrameSource::flushConsumed()
@@ -188,10 +322,10 @@ void AES3AudioSubFrameSource::flushConsumed()
         aes3AudioBuffer->flushConsumed();
 }
 
-void AES3AudioSubFrameSource::tagConsumed(unsigned count)
+void AES3AudioSubFrameSource::tagVirtualConsumed(vlc_tick_t from, unsigned count)
 {
     if(aes3AudioBuffer)
-        aes3AudioBuffer->tagConsumed(count);
+        aes3AudioBuffer->tagVirtualConsumed(from, count);
 }
 
 void AES3AudioSubFrameSource::forwardTo(vlc_tick_t t)
@@ -210,11 +344,25 @@ bool AES3AudioSubFrameSource::available() const
     return aes3AudioBuffer == NULL;
 }
 
-unsigned AES3AudioSubFrameSource::availableSamples(vlc_tick_t from) const
+vlc_fourcc_t AES3AudioSubFrameSource::getCodec() const
+{
+    if(aes3AudioBuffer == NULL)
+        return 0;
+    return aes3AudioBuffer->getCodec();
+}
+
+unsigned AES3AudioSubFrameSource::availableVirtualSamples(vlc_tick_t from) const
 {
     if(aes3AudioBuffer == NULL)
         return 0;
-    return aes3AudioBuffer->availableSamples(from);
+    return aes3AudioBuffer->availableVirtualSamples(from);
+}
+
+unsigned AES3AudioSubFrameSource::alignedInterleaveInSamples(vlc_tick_t from, unsigned n) const
+{
+    if(aes3AudioBuffer == NULL)
+        return 0;
+    return aes3AudioBuffer->alignedInterleaveInSamples(from, n);
 }
 
 AES3AudioFrameSource::AES3AudioFrameSource()
@@ -242,14 +390,24 @@ unsigned AES3AudioFrameSource::samplesUpToTime(vlc_tick_t t) const
     return diff / (48000 * 2 * 2);
 }
 
-unsigned AES3AudioFrameSource::availableSamples(vlc_tick_t from) const
+unsigned AES3AudioFrameSource::availableVirtualSamples(vlc_tick_t from) const
 {
     if(!subframe0.available() && !subframe1.available())
-        return std::min(subframe0.availableSamples(from), subframe1.availableSamples(from));
+        return std::min(subframe0.availableVirtualSamples(from), subframe1.availableVirtualSamples(from));
     else if(subframe1.available())
-        return subframe0.availableSamples(from);
+        return subframe0.availableVirtualSamples(from);
     else
-        return subframe1.availableSamples(from);
+        return subframe1.availableVirtualSamples(from);
+}
+
+unsigned AES3AudioFrameSource::alignedInterleaveInSamples(vlc_tick_t from, unsigned i_wanted) const
+{
+    unsigned a0 = i_wanted, a1 = i_wanted;
+    if(!subframe0.available())
+        a0 = subframe0.alignedInterleaveInSamples(from, i_wanted);
+    if(!subframe1.available())
+        a1 = subframe1.alignedInterleaveInSamples(from, i_wanted);
+    return std::max(a0, a1);
 }
 
 void AES3AudioFrameSource::flushConsumed()
@@ -258,10 +416,10 @@ void AES3AudioFrameSource::flushConsumed()
     subframe1.flushConsumed();
 }
 
-void AES3AudioFrameSource::tagConsumed(unsigned samples)
+void AES3AudioFrameSource::tagVirtualConsumed(vlc_tick_t from, unsigned samples)
 {
-    subframe0.tagConsumed(samples);
-    subframe1.tagConsumed(samples);
+    subframe0.tagVirtualConsumed(from, samples);
+    subframe1.tagVirtualConsumed(from, samples);
 }
 
 void AES3AudioFrameSource::forwardTo(vlc_tick_t t)
diff --git a/modules/stream_out/sdi/AES3Audio.hpp b/modules/stream_out/sdi/AES3Audio.hpp
index c4f90dbf91..f18fb58271 100644
--- a/modules/stream_out/sdi/AES3Audio.hpp
+++ b/modules/stream_out/sdi/AES3Audio.hpp
@@ -24,6 +24,7 @@
 #include <vlc_block.h>
 #include <vlc_block_helper.h>
 #include <mutex>
+#include <vlc_es.h>
 
 #define MAX_AES3_AUDIO_FRAMES     8
 #define MAX_AES3_AUDIO_SUBFRAMES (MAX_AES3_AUDIO_FRAMES * 2)
@@ -43,29 +44,36 @@ namespace sdi_sout
     class AES3AudioBuffer
     {
         public:
-            AES3AudioBuffer(unsigned = 0);
+            AES3AudioBuffer(vlc_object_t *, unsigned = 0);
             ~AES3AudioBuffer();
             void setSubFramesCount(uint8_t);
             vlc_tick_t bufferStart() const;
             vlc_tick_t bufferEnd() const;
-            unsigned availableSamples(vlc_tick_t) const;
+            unsigned availableVirtualSamples(vlc_tick_t) const;
+            unsigned alignedInterleaveInSamples(vlc_tick_t, unsigned) const;
             void push(block_t *);
-            void read(void *, unsigned, unsigned,
-                      const AES3AudioSubFrameIndex &,
-                      const AES3AudioSubFrameIndex &, unsigned);
+            unsigned read(void *, unsigned, vlc_tick_t,
+                          const AES3AudioSubFrameIndex &,
+                          const AES3AudioSubFrameIndex &, unsigned);
             void flushConsumed();
-            void tagConsumed(unsigned);
+            void tagVirtualConsumed(vlc_tick_t, unsigned);
             void forwardTo(vlc_tick_t);
+            void setCodec(vlc_fourcc_t);
+            vlc_fourcc_t getCodec() const;
 
         private:
+            vlc_object_t *obj;
+            void tagConsumed(unsigned);
             size_t   FramesToBytes(unsigned) const;
             vlc_tick_t FramesToDuration(unsigned) const;
+            int OffsetToBufferStart(vlc_tick_t t) const;
             unsigned BytesToFrames(size_t) const;
             unsigned TicksDurationToFrames(vlc_tick_t) const;
             block_bytestream_t bytestream;
             mutable std::mutex bytestream_mutex;
             uint8_t buffersubframes;
             unsigned toconsume;
+            vlc_fourcc_t i_codec;
     };
 
     class AES3AudioSubFrameSource
@@ -74,14 +82,16 @@ namespace sdi_sout
             AES3AudioSubFrameSource();
             AES3AudioSubFrameSource(AES3AudioBuffer *, AES3AudioSubFrameIndex);
             vlc_tick_t bufferStartTime() const;
-            void copy(void *, unsigned count, unsigned,
-                      const AES3AudioSubFrameIndex &, unsigned width);
+            unsigned copy(void *, unsigned count, vlc_tick_t,
+                          const AES3AudioSubFrameIndex &, unsigned width);
             void flushConsumed();
-            void tagConsumed(unsigned);
+            void tagVirtualConsumed(vlc_tick_t, unsigned);
             void forwardTo(vlc_tick_t t);
-            unsigned availableSamples(vlc_tick_t) const;
+            unsigned availableVirtualSamples(vlc_tick_t) const;
+            unsigned alignedInterleaveInSamples(vlc_tick_t, unsigned) const;
             const AES3AudioSubFrameIndex & index() const;
             bool available() const;
+            vlc_fourcc_t getCodec() const;
 
         private:
             AES3AudioBuffer *aes3AudioBuffer;
@@ -94,9 +104,10 @@ namespace sdi_sout
             AES3AudioFrameSource();
             vlc_tick_t bufferStartTime() const;
             unsigned samplesUpToTime(vlc_tick_t) const;
-            unsigned availableSamples(vlc_tick_t) const;
+            unsigned availableVirtualSamples(vlc_tick_t) const;
+            unsigned alignedInterleaveInSamples(vlc_tick_t, unsigned) const;
             void flushConsumed();
-            void tagConsumed(unsigned);
+            void tagVirtualConsumed(vlc_tick_t, unsigned);
             void forwardTo(vlc_tick_t t);
             AES3AudioSubFrameSource subframe0;
             AES3AudioSubFrameSource subframe1;
diff --git a/modules/stream_out/sdi/DBMSDIOutput.cpp b/modules/stream_out/sdi/DBMSDIOutput.cpp
index 57987276db..a513e13798 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.cpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.cpp
@@ -505,13 +505,28 @@ int DBMSDIOutput::Process()
     while((p = reinterpret_cast<picture_t *>(videoBuffer.Dequeue())))
     {
         vlc_tick_t bufferStart = audioMultiplex->bufferStart();
+        unsigned i_samples_per_frame =
+                audioMultiplex->alignedInterleaveInSamples(bufferStart, SAMPLES_PER_FRAME);
+
+#ifdef SDI_MULTIPLEX_DEBUG
+        audioMultiplex->Debug();
+#endif
+
         while(bufferStart <= p->date &&
-              audioMultiplex->availableSamples(bufferStart) >= SAMPLES_PER_FRAME)
+              audioMultiplex->availableVirtualSamples(bufferStart) >= i_samples_per_frame)
         {
-              block_t *out = audioMultiplex->Extract(SAMPLES_PER_FRAME);
+            block_t *out = audioMultiplex->Extract(i_samples_per_frame);
             if(out)
+            {
+#ifdef SDI_MULTIPLEX_DEBUG
+                  msg_Dbg(p_stream, "extracted %u samples pts %ld i_samples_per_frame %u",
+                          out->i_nb_samples, out->i_dts, i_samples_per_frame);
+#endif
                   ProcessAudio(out);
+            }
             else break;
+            bufferStart = audioMultiplex->bufferStart();
+            i_samples_per_frame = audioMultiplex->alignedInterleaveInSamples(bufferStart, SAMPLES_PER_FRAME);
         }
 
         ProcessVideo(p, reinterpret_cast<block_t *>(captionsBuffer.Dequeue()));
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.cpp b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
index 25cc4b4173..f6f8542fb5 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.cpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
@@ -29,8 +29,8 @@
 
 using namespace sdi_sout;
 
-SDIAudioMultiplexBuffer::SDIAudioMultiplexBuffer()
-    : AES3AudioBuffer(2), AbstractStreamOutputBuffer()
+SDIAudioMultiplexBuffer::SDIAudioMultiplexBuffer(vlc_object_t *obj)
+    : AES3AudioBuffer(obj, 2), AbstractStreamOutputBuffer()
 {
 
 }
@@ -75,14 +75,15 @@ static void ConfigureChannels(unsigned i, es_format_t *fmt)
     fmt->audio.i_blockalign = i * 16 / 8;
 }
 
-SDIAudioMultiplexConfig::Mapping::Mapping(const StreamID &id)
-    : id(id)
+SDIAudioMultiplexConfig::Mapping::Mapping(vlc_object_t *obj, const StreamID &id)
+    : id(id), buffer(obj)
 {
     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);
+    b_decode = true;
 }
 
 SDIAudioMultiplexConfig::Mapping::~Mapping()
@@ -90,8 +91,9 @@ SDIAudioMultiplexConfig::Mapping::~Mapping()
     es_format_Clean(&fmt);
 }
 
-SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(uint8_t channels)
+SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(vlc_object_t *obj, uint8_t channels)
 {
+    this->obj = obj;
     subframeslotbitmap = 0;
     if(channels > 4)
         framewidth = 8;
@@ -108,6 +110,14 @@ SDIAudioMultiplexConfig::~SDIAudioMultiplexConfig()
         delete mappings[i];
 }
 
+bool SDIAudioMultiplexConfig::decode(const StreamID &id) const
+{
+    const Mapping *map = getMappingByID(id);
+    if(map)
+        return map->b_decode;
+    return true;
+}
+
 bool SDIAudioMultiplexConfig::SubFrameSlotUsed(uint8_t i) const
 {
     return (1 << i) & subframeslotbitmap;
@@ -155,11 +165,17 @@ void SDIAudioMultiplexConfig::parseConfiguration(vlc_object_t *obj, const char *
                 {
                     msg_Dbg(obj,"found declaration for ES %s %d",
                                 (i_id > -1) ? "pid #" : "seq", *pi_id);
+                    bool b_embed = false;
                     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())
+                        if(!std::strcmp("embed", p->psz_name))
+                        {
+                            b_embed = true;
+                            msg_Dbg(obj," * mode passthrough set");
+                        }
+                        else if(!std::strcmp("chans", p->psz_name) && subframeslots.empty())
                         {
                             char *end = NULL;
                             int i_val = std::strtol(p->psz_value, &end, 10);
@@ -190,7 +206,9 @@ void SDIAudioMultiplexConfig::parseConfiguration(vlc_object_t *obj, const char *
                     }
 
                     bool b_success = false;
-                    if(subframeslots.empty() && i_reserved_chans)
+                    if(b_embed)
+                        b_success = addMappingEmbed(StreamID(i_id, i_seqid));
+                    else 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);
@@ -210,7 +228,7 @@ void SDIAudioMultiplexConfig::parseConfiguration(vlc_object_t *obj, const char *
     }
 }
 
-std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
+std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots(bool b_aligned) const
 {
     std::vector<uint8_t> slots;
     for(uint8_t i=0; i<getMultiplexedFramesCount() * 2; i++)
@@ -219,6 +237,13 @@ std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
             slots.push_back(i);
     }
 
+    for( ; b_aligned && slots.size() >= 2; slots.erase(slots.begin()))
+    {
+        /* get aligned subframes pair */
+        if((slots[0] & 1) == 0 && slots[1] == slots[0] + 1)
+            break;
+    }
+
     return slots;
 }
 
@@ -248,6 +273,19 @@ bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, unsigned channels)
     return addMapping(id, slots);
 }
 
+bool SDIAudioMultiplexConfig::addMappingEmbed(const StreamID &id, std::vector<uint8_t> slots)
+{
+    if(slots.empty())
+        slots = getFreeSubFrameSlots(true);
+    if(slots.size() < 2)
+        return false;
+    slots.resize(2);
+    bool b = addMapping(id, slots);
+    if(b)
+        getMappingByID(id)->b_decode = false;
+    return b;
+}
+
 bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t> subframeslots)
 {
     for(size_t i=0; i<mappings.size(); i++)
@@ -257,7 +295,7 @@ bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t
         if(SubFrameSlotUsed(subframeslots[i]))
             return false;
 
-    Mapping *assoc = new Mapping(id);
+    Mapping *assoc = new Mapping(obj, id);
     assoc->subframesslots = subframeslots;
 
     mappings.push_back(assoc);
@@ -318,9 +356,10 @@ const es_format_t *
     return NULL;
 }
 
-SDIAudioMultiplex::SDIAudioMultiplex(uint8_t channels)
+SDIAudioMultiplex::SDIAudioMultiplex(vlc_object_t *obj, uint8_t channels)
+    : config(SDIAudioMultiplexConfig(obj, channels))
 {
-    config = SDIAudioMultiplexConfig(channels);
+    p_obj = obj;
     head = VLC_TICK_INVALID;
 }
 
@@ -329,7 +368,7 @@ SDIAudioMultiplex::~SDIAudioMultiplex()
 
 }
 
-unsigned SDIAudioMultiplex::availableSamples(vlc_tick_t from) const
+unsigned SDIAudioMultiplex::availableVirtualSamples(vlc_tick_t from) const
 {
     unsigned samples = std::numeric_limits<unsigned>::max();
     for(size_t i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
@@ -337,11 +376,24 @@ unsigned SDIAudioMultiplex::availableSamples(vlc_tick_t from) const
         if(framesources[i].subframe0.available() &&
            framesources[i].subframe1.available())
             continue;
-        samples = std::min(samples, framesources[i].availableSamples(from));
+        samples = std::min(samples, framesources[i].availableVirtualSamples(from));
     }
     return samples < std::numeric_limits<unsigned>::max() ? samples : 0;
 }
 
+unsigned SDIAudioMultiplex::alignedInterleaveInSamples(vlc_tick_t from, unsigned i_wanted) const
+{
+    unsigned i_align = i_wanted;
+    for(size_t i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+    {
+        if(!framesources[i].subframe0.available())
+            i_align = std::min(i_align, framesources[i].subframe0.alignedInterleaveInSamples(from, i_wanted));
+        if(!framesources[i].subframe1.available())
+            i_align = std::min(i_align, framesources[i].subframe1.alignedInterleaveInSamples(from, i_wanted));
+    }
+    return i_align;
+}
+
 vlc_tick_t SDIAudioMultiplex::bufferStart() const
 {
     vlc_tick_t start = VLC_TICK_INVALID;
@@ -382,7 +434,8 @@ void SDIAudioMultiplex::SetSubFrameSource(uint8_t n, AES3AudioBuffer *buf,
     *s = AES3AudioSubFrameSource(buf, idx);
 }
 
-void SDIAudioMultiplex::Debug(vlc_object_t *p_obj) const
+#ifdef SDI_MULTIPLEX_DEBUG
+void SDIAudioMultiplex::Debug() const
 {
     msg_Dbg(p_obj, "Multiplex: head %ld bufferstart() %ld", head, bufferStart());
     for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
@@ -394,6 +447,7 @@ void SDIAudioMultiplex::Debug(vlc_object_t *p_obj) const
             msg_Dbg(p_obj, " [%d.1] bufferstart() %ld", i, source->subframe1.bufferStartTime());
     }
 }
+#endif
 
 block_t * SDIAudioMultiplex::Extract(unsigned samples)
 {
@@ -402,10 +456,14 @@ block_t * SDIAudioMultiplex::Extract(unsigned samples)
     uint8_t interleavedframes = config.getMultiplexedFramesCount();
 
     /* Ensure we never roll back due to late fifo */
-    if(head != VLC_TICK_INVALID && start > head)
+    if(head != VLC_TICK_INVALID)
     {
-        for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
-            framesources[i].forwardTo(head);
+        if(start < head)
+        {
+            for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
+                framesources[i].forwardTo(head);
+        }
+        start = head;
     }
 
     block_t *p_block = block_Alloc( interleavedframes * 2 * sizeof(uint16_t) * samples );
@@ -419,24 +477,24 @@ block_t * SDIAudioMultiplex::Extract(unsigned samples)
     for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
     {
         AES3AudioFrameSource *source = &framesources[i];
-        unsigned avail = source->availableSamples(start);
-        if(avail == 0)
+        unsigned ahead = source->availableVirtualSamples(start);
+        if(ahead == 0)
             continue;
 
-        unsigned toskip = 0;
-        unsigned tocopy = std::min(samples, avail);
-
-        toskip = source->samplesUpToTime(start);
-        if(toskip >= tocopy)
-            continue;
-        tocopy -= toskip;
+#ifdef SDI_MULTIPLEX_DEBUG
+        vlc_fourcc_t i_codec = source->subframe0.getCodec();
+        msg_Dbg(p_obj, "%4.4s pair %u tocopy %u from %ld head %ld, avail %u",
+                reinterpret_cast<const char *>(&i_codec), i, samples,
+                start, source->bufferStartTime(), ahead);
+#endif
 
-        source->subframe0.copy(p_block->p_buffer, tocopy, toskip, (i * 2 + 0), interleavedframes);
-        source->subframe1.copy(p_block->p_buffer, tocopy, toskip, (i * 2 + 1), interleavedframes);
+        source->subframe0.copy(p_block->p_buffer, samples, start, (i * 2 + 0), interleavedframes);
+        source->subframe1.copy(p_block->p_buffer, samples, start, (i * 2 + 1), interleavedframes);
     }
 
+
     for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
-        framesources[i].tagConsumed(samples);
+        framesources[i].tagVirtualConsumed(start, samples);
     for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
         framesources[i].flushConsumed();
 
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.hpp b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
index c37f4bbf10..ea9f2d26d8 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.hpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
@@ -22,6 +22,7 @@
 
 #include "AES3Audio.hpp"
 #include "SDIStream.hpp"
+#include "sdiout.hpp"
 
 #include <vector>
 
@@ -33,7 +34,7 @@ namespace sdi_sout
                                     public AbstractStreamOutputBuffer
     {
         public:
-            SDIAudioMultiplexBuffer();
+            SDIAudioMultiplexBuffer(vlc_object_t *);
             virtual ~SDIAudioMultiplexBuffer();
             virtual void FlushQueued(); /* impl */
             virtual void Enqueue(void *); /* impl */
@@ -43,21 +44,23 @@ namespace sdi_sout
     class SDIAudioMultiplexConfig
     {
         public:
-            SDIAudioMultiplexConfig(uint8_t channels = 2);
+            SDIAudioMultiplexConfig(vlc_object_t *obj, 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 decode(const StreamID &) const;
             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> getFreeSubFrameSlots(bool = false) 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>);
+            bool addMappingEmbed(const StreamID &, std::vector<uint8_t> = std::vector<uint8_t>());
             unsigned getMaxSamplesForBlockSize(size_t) const;
 
         private:
@@ -65,10 +68,11 @@ namespace sdi_sout
             class Mapping
             {
                 public:
-                    Mapping(const StreamID &);
+                    Mapping(vlc_object_t *, const StreamID &);
                     ~Mapping();
                     StreamID id;
                     es_format_t fmt;
+                    bool b_decode;
                     SDIAudioMultiplexBuffer buffer;
                     std::vector<uint8_t> subframesslots;
             };
@@ -78,24 +82,29 @@ namespace sdi_sout
             unsigned subframeslotbitmap;
             uint8_t framewidth;
             bool b_accept_any;
+            vlc_object_t *obj;
     };
 
     class SDIAudioMultiplex
     {
         public:
-            SDIAudioMultiplex(uint8_t channels);
+            SDIAudioMultiplex(vlc_object_t *, uint8_t channels);
             ~SDIAudioMultiplex();
             vlc_tick_t bufferStart() const;
-            unsigned availableSamples(vlc_tick_t) const;
+            unsigned availableVirtualSamples(vlc_tick_t) const;
+            unsigned alignedInterleaveInSamples(vlc_tick_t, unsigned) const;
             block_t * Extract(unsigned);
             unsigned getFreeSubFrameSlots() const;
             void SetSubFrameSource(uint8_t, AES3AudioBuffer *, AES3AudioSubFrameIndex);
-            void Debug(vlc_object_t *) const;
+#ifdef SDI_MULTIPLEX_DEBUG
+            void Debug() const;
+#endif
 
             SDIAudioMultiplexConfig config;
             vlc_tick_t head;
 
         private:
+            vlc_object_t *p_obj;
             unsigned count;
             AES3AudioFrameSource framesources[MAX_AES3_AUDIO_FRAMES];
     };
diff --git a/modules/stream_out/sdi/SDIOutput.cpp b/modules/stream_out/sdi/SDIOutput.cpp
index c08ae4de2f..127ecf3519 100644
--- a/modules/stream_out/sdi/SDIOutput.cpp
+++ b/modules/stream_out/sdi/SDIOutput.cpp
@@ -54,7 +54,8 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
     program = -1;
     videoStream = NULL;
     captionsStream = NULL;
-    audioMultiplex = new SDIAudioMultiplex( var_InheritInteger(p_stream, CFG_PREFIX "channels") );
+    audioMultiplex = new SDIAudioMultiplex(VLC_OBJECT(p_stream),
+                                           var_InheritInteger(p_stream, CFG_PREFIX "channels"));
     char *psz_channelsconf = var_InheritString(p_stream, CFG_PREFIX "audio");
     if(psz_channelsconf)
     {
@@ -110,11 +111,15 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
             SDIAudioMultiplexBuffer *buffer = audioMultiplex->config.getBufferForStream(id);
             if(!buffer)
                 return NULL;
-            AudioDecodedStream *audioStream;
-            s = audioStream = dynamic_cast<AudioDecodedStream *>(createStream(id, fmt, buffer));
-            if(audioStream)
+
+            s = createStream(id, fmt, buffer, audioMultiplex->config.decode(id));
+            if(s)
             {
-                audioStream->setOutputFormat(cfgfmt);
+                if(!audioMultiplex->config.decode(id))
+                    buffer->setCodec(fmt->i_codec);
+                AudioDecodedStream *audioStream = dynamic_cast<AudioDecodedStream *>(s);
+                if(audioStream)
+                    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++)
@@ -128,7 +133,7 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
     }
     else if(fmt->i_cat == SPU_ES && !captionsStream)
     {
-        s = captionsStream = dynamic_cast<CaptionsStream *>(createStream(id, fmt, &captionsBuffer));
+        s = captionsStream = dynamic_cast<CaptionsStream *>(createStream(id, fmt, &captionsBuffer, false));
     }
 
     if(program == -1)
@@ -166,17 +171,24 @@ int SDIOutput::Control(int, va_list)
 
 AbstractStream *SDIOutput::createStream(const StreamID &id,
                                         const es_format_t *fmt,
-                                        AbstractStreamOutputBuffer *buffer)
+                                        AbstractStreamOutputBuffer *buffer,
+                                        bool b_decoded)
 {
-    AbstractStream *s;
-    if(fmt->i_cat == VIDEO_ES)
-        s = new VideoDecodedStream(VLC_OBJECT(p_stream), id, buffer);
-    else if(fmt->i_cat == AUDIO_ES)
-        s = new AudioDecodedStream(VLC_OBJECT(p_stream), id, buffer);
-    else if(fmt->i_cat == SPU_ES)
-        s = new CaptionsStream(VLC_OBJECT(p_stream), id, buffer);
+    AbstractStream *s = NULL;
+    if(b_decoded)
+    {
+        if(fmt->i_cat == VIDEO_ES)
+            s = new VideoDecodedStream(VLC_OBJECT(p_stream), id, buffer);
+        else if(fmt->i_cat == AUDIO_ES)
+            s = new AudioDecodedStream(VLC_OBJECT(p_stream), id, buffer);
+    }
     else
-        s = NULL;
+    {
+        if(fmt->i_cat == AUDIO_ES)
+            s = new AudioCompressedStream(VLC_OBJECT(p_stream), id, buffer);
+        else if(fmt->i_cat == SPU_ES)
+            s = new CaptionsStream(VLC_OBJECT(p_stream), id, buffer);
+    }
 
      if(s && !s->init(fmt))
      {
diff --git a/modules/stream_out/sdi/SDIOutput.hpp b/modules/stream_out/sdi/SDIOutput.hpp
index b138d5227d..c21f5b09e9 100644
--- a/modules/stream_out/sdi/SDIOutput.hpp
+++ b/modules/stream_out/sdi/SDIOutput.hpp
@@ -43,12 +43,13 @@ namespace sdi_sout
         protected:
             virtual AbstractStream * createStream(const StreamID &,
                                                   const es_format_t *,
-                                                  AbstractStreamOutputBuffer *);
+                                                  AbstractStreamOutputBuffer *,
+                                                  bool = true);
             virtual int ConfigureVideo(const video_format_t *) = 0;
             virtual int ConfigureAudio(const audio_format_t *) = 0;
             sout_stream_t *p_stream;
             VideoDecodedStream *videoStream;
-            std::list<AudioDecodedStream *> audioStreams;
+            std::list<AbstractStream *> audioStreams;
             CaptionsStream *captionsStream;
             PictureStreamOutputBuffer videoBuffer;
             BlockStreamOutputBuffer captionsBuffer;
diff --git a/modules/stream_out/sdi/SDIStream.cpp b/modules/stream_out/sdi/SDIStream.cpp
index 9819a9c663..9c477d34fa 100644
--- a/modules/stream_out/sdi/SDIStream.cpp
+++ b/modules/stream_out/sdi/SDIStream.cpp
@@ -566,6 +566,39 @@ void AbstractRawStream::FlushQueued()
         block_Release(p);
 }
 
+AudioCompressedStream::AudioCompressedStream(vlc_object_t *p_obj, const StreamID &id,
+                                             AbstractStreamOutputBuffer *buffer)
+    : AbstractRawStream(p_obj, id, buffer)
+{
+}
+
+AudioCompressedStream::~AudioCompressedStream()
+{
+}
+
+int AudioCompressedStream::Send(block_t *p_block)
+{
+    const size_t i_payload = p_block->i_buffer;
+    const size_t i_pad = (p_block->i_buffer & 1) ? 1 : 0;
+    p_block = block_Realloc(p_block, 12, p_block->i_buffer + i_pad);
+    if(!p_block)
+        return VLC_EGENERIC;
+    /* Convert to AES3 Payload */
+    SetWBE(&p_block->p_buffer[0], 0x0000); /* Extra 0000 */
+    SetWBE(&p_block->p_buffer[2], 0x0000); /* see S337 Annex B */
+    SetWBE(&p_block->p_buffer[4], 0xF872); /* Pa Start code/Preamble */
+    SetWBE(&p_block->p_buffer[6], 0x4E1F); /* Pb Start code/Preamble */
+    SetWBE(&p_block->p_buffer[8], 0x0001); /* A52 Burst code */
+    SetWBE(&p_block->p_buffer[10], i_payload);
+    if(i_pad)
+        p_block->p_buffer[p_block->i_buffer - 1] = 0x00;
+    return AbstractRawStream::Send(p_block);
+}
+
+bool AudioCompressedStream::init(const es_format_t *fmt)
+{
+    return (fmt->i_codec == VLC_CODEC_A52);
+}
 
 CaptionsStream::CaptionsStream(vlc_object_t *p_obj, const StreamID &id,
                                AbstractStreamOutputBuffer *buffer)
diff --git a/modules/stream_out/sdi/SDIStream.hpp b/modules/stream_out/sdi/SDIStream.hpp
index 67cef9af43..4f962a247c 100644
--- a/modules/stream_out/sdi/SDIStream.hpp
+++ b/modules/stream_out/sdi/SDIStream.hpp
@@ -172,6 +172,16 @@ namespace sdi_sout
             void FlushQueued();
     };
 
+    class AudioCompressedStream : public AbstractRawStream
+    {
+        public:
+            AudioCompressedStream(vlc_object_t *, const StreamID &,
+                                  AbstractStreamOutputBuffer *);
+            virtual ~AudioCompressedStream();
+            virtual int Send(block_t*); /* reimpl */
+            virtual bool init(const es_format_t *); /* impl */
+    };
+
     class CaptionsStream : public AbstractRawStream
     {
         public:
diff --git a/modules/stream_out/sdi/sdiout.cpp b/modules/stream_out/sdi/sdiout.cpp
index c7b3f88200..ef47f10538 100644
--- a/modules/stream_out/sdi/sdiout.cpp
+++ b/modules/stream_out/sdi/sdiout.cpp
@@ -80,6 +80,7 @@
                        "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. " \
+                       "{embed} to request compressed passthrough in PCM (hack). " \
                        "Use 'only' to accept only declared ES. " \
                        "ex: only:#145{0,1}:#142{2,3}:2{chans=6} "
 
diff --git a/modules/stream_out/sdi/sdiout.hpp b/modules/stream_out/sdi/sdiout.hpp
index 9444ba35cf..d7f18f75b8 100644
--- a/modules/stream_out/sdi/sdiout.hpp
+++ b/modules/stream_out/sdi/sdiout.hpp
@@ -21,3 +21,5 @@
 #define CFG_PREFIX "sdiout-"
 
 #define FAKE_DRIVER 0
+
+//#define SDI_MULTIPLEX_DEBUG




More information about the vlc-commits mailing list