[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