vlc | branch: master | Francois Cartegnie <[email protected]> | 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 _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
