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