[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