[vlc-commits] sout: sdi: add support for audio pairs remapping
Francois Cartegnie
git at videolan.org
Wed Sep 5 10:36:45 CEST 2018
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Fri Aug 31 18:21:15 2018 +0200| [989a9db6668ca05d158bd6197274bfca81b7c121] | committer: Francois Cartegnie
sout: sdi: add support for audio pairs remapping
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=989a9db6668ca05d158bd6197274bfca81b7c121
---
modules/stream_out/sdi/DBMSDIOutput.cpp | 11 +-
modules/stream_out/sdi/DBMSDIOutput.hpp | 1 -
modules/stream_out/sdi/SDIAudioMultiplex.cpp | 179 +++++++++++++++++++++++++++
modules/stream_out/sdi/SDIAudioMultiplex.hpp | 10 ++
modules/stream_out/sdi/SDIOutput.cpp | 31 +++--
modules/stream_out/sdi/SDIOutput.hpp | 2 +-
modules/stream_out/sdi/sdiout.cpp | 10 ++
7 files changed, 220 insertions(+), 24 deletions(-)
diff --git a/modules/stream_out/sdi/DBMSDIOutput.cpp b/modules/stream_out/sdi/DBMSDIOutput.cpp
index d2e602fd36..0818e4fc03 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.cpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.cpp
@@ -54,7 +54,6 @@ 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;
@@ -274,15 +273,6 @@ 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;
@@ -302,6 +292,7 @@ int DBMSDIOutput::ConfigureAudio(const audio_format_t *)
maxchannels,
bmdAudioOutputStreamTimestamped);
CHECK("Could not start audio output");
+ audio.b_configured = true;
}
return VLC_SUCCESS;
diff --git a/modules/stream_out/sdi/DBMSDIOutput.hpp b/modules/stream_out/sdi/DBMSDIOutput.hpp
index 7c1de10bf2..13a65fd29f 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.hpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.hpp
@@ -53,7 +53,6 @@ namespace sdi_sout
vlc_tick_t lasttimestamp;
/* XXX: workaround card clock drift */
vlc_tick_t offset;
-
bool b_running;
int Start();
const char *ErrorToString(long i_code);
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.cpp b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
index 35993947c5..4369932f64 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.cpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
@@ -22,7 +22,10 @@
#endif
#include "SDIAudioMultiplex.hpp"
+#include <vlc_es.h>
#include <limits>
+#include <cstring>
+#include <algorithm>
using namespace sdi_sout;
@@ -52,10 +55,39 @@ void * SDIAudioMultiplexBuffer::Dequeue()
return NULL;
}
+static void ConfigureChannels(unsigned i, es_format_t *fmt)
+{
+ if( i>=8 )
+ {
+ i = 8;
+ fmt->audio.i_physical_channels = AOUT_CHANS_7_1;
+ }
+ else if( i>2 )
+ {
+ i = 6;
+ fmt->audio.i_physical_channels = AOUT_CHANS_5_1;
+ }
+ else
+ {
+ fmt->audio.i_physical_channels = AOUT_CHANS_STEREO;
+ }
+ fmt->audio.i_channels = i;
+ fmt->audio.i_blockalign = i * 16 / 8;
+}
+
SDIAudioMultiplexConfig::Mapping::Mapping(const StreamID &id)
: id(id)
{
+ 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);
+}
+SDIAudioMultiplexConfig::Mapping::~Mapping()
+{
+ es_format_Clean(&fmt);
}
SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(uint8_t channels)
@@ -67,6 +99,7 @@ SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(uint8_t channels)
framewidth = 4;
else
framewidth = 1;
+ b_accept_any = true;
}
SDIAudioMultiplexConfig::~SDIAudioMultiplexConfig()
@@ -85,6 +118,98 @@ void SDIAudioMultiplexConfig::setSubFrameSlotUsed(uint8_t i)
subframeslotbitmap |= (1 << i);
}
+void SDIAudioMultiplexConfig::parseConfiguration(vlc_object_t *obj, const char *psz)
+{
+ char *name = NULL;
+ char *psz_in = (char*)psz;
+ config_chain_t *p_config_chain = NULL;
+ while(psz_in)
+ {
+ char *psz_next = config_ChainCreate(&name, &p_config_chain, psz_in);
+ if(name)
+ {
+ if(!std::strcmp(name, "only"))
+ {
+ b_accept_any = false;
+ msg_Dbg(obj, "only accepting declared streams");
+ }
+ else /* try mapping decl */
+ {
+ int i_id = -1;
+ int i_seqid = -1;
+ int *pi_id = &i_seqid;
+ const char *psz_id = name;
+ if(psz_id[0]=='#')
+ {
+ psz_id++;
+ pi_id = &i_id;
+ }
+ if(*psz_id)
+ {
+ char *end = NULL;
+ int i_val = std::strtol(psz_id, &end, 10);
+ if(end != NULL && *end == '\0')
+ *pi_id = i_val;
+ }
+ if(i_id != -1 || i_seqid != -1)
+ {
+ msg_Dbg(obj,"found declaration for ES %s %d",
+ (i_id > -1) ? "pid #" : "seq", *pi_id);
+ 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())
+ {
+ char *end = NULL;
+ int i_val = std::strtol(p->psz_value, &end, 10);
+ if(end != NULL && *end == '\0')
+ {
+ i_reserved_chans = i_val;
+ msg_Dbg(obj," * provisioned %d channels", i_val);
+ }
+ else msg_Warn(obj, " * ignoring channels count declaration %d", i_val);
+ }
+ else if(i_reserved_chans == 0)
+ {
+ char *end = NULL;
+ int i_slot = std::strtol(p->psz_name, &end, 10);
+ if(end != NULL && *end == '\0')
+ {
+ if(i_slot < MAX_AES3_AUDIO_SUBFRAMES && i_slot < (2 * framewidth) &&
+ std::find(subframeslots.begin(), subframeslots.end(), i_slot) == subframeslots.end())
+ {
+ subframeslots.push_back(i_slot);
+ msg_Dbg(obj," * mapped channel %zd to subframe %d",
+ subframeslots.size(), i_slot);
+ }
+ else msg_Warn(obj, " * ignoring invalid subframe declaration %d", i_slot);
+ }
+ else msg_Warn(obj, " * ignoring unknown/invalid token %s", p->psz_name);
+ }
+ }
+
+ bool b_success = false;
+ 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);
+
+ if(b_success)
+ msg_Dbg(obj, " * successfully configured");
+ else
+ msg_Warn(obj, " * configuration rejected (duplicate or not enough subframes ?)");
+ }
+ }
+ free(name);
+ }
+ config_ChainDestroy(p_config_chain);
+ if(psz != psz_in)
+ free(psz_in);
+ psz_in = psz_next;
+ }
+}
+
std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
{
std::vector<uint8_t> slots;
@@ -97,6 +222,32 @@ std::vector<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots() const
return slots;
}
+std::vector<uint8_t> SDIAudioMultiplexConfig::getConfiguredSlots(const StreamID &id) const
+{
+ for(size_t i=0; i<mappings.size(); i++)
+ {
+ if(mappings[i]->id == id)
+ return mappings[i]->subframesslots;
+ }
+ return std::vector<uint8_t>();
+}
+
+bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, const es_format_t *fmt)
+{
+ if(!fmt->audio.i_channels || !b_accept_any)
+ return false;
+ return addMapping(id, fmt->audio.i_channels);
+}
+
+bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, unsigned channels)
+{
+ std::vector<uint8_t> slots = getFreeSubFrameSlots();
+ if(slots.size() < channels)
+ return false;
+ slots.resize(channels);
+ return addMapping(id, slots);
+}
+
bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t> subframeslots)
{
for(size_t i=0; i<mappings.size(); i++)
@@ -111,6 +262,9 @@ bool SDIAudioMultiplexConfig::addMapping(const StreamID &id, std::vector<uint8_t
mappings.push_back(assoc);
+ for(size_t i=0; i<subframeslots.size(); i++)
+ setSubFrameSlotUsed(subframeslots[i]);
+
return true;
}
@@ -130,6 +284,31 @@ SDIAudioMultiplexBuffer *
return NULL;
}
+const es_format_t * SDIAudioMultiplexConfig::getConfigurationForStream(const StreamID &id) const
+{
+ auto it = std::find_if(mappings.begin(), mappings.end(),
+ [&id](Mapping *e) { return e->id == id; });
+ return (it != mappings.end()) ? &(*it)->fmt : NULL;
+}
+
+const es_format_t *
+ SDIAudioMultiplexConfig::updateFromRealESConfig(const StreamID &id,
+ const es_format_t *fmt)
+{
+ auto it = std::find_if(mappings.begin(), mappings.end(),
+ [&id](Mapping *e) { return e->id == id; });
+ if(it != mappings.end())
+ {
+ Mapping *mapping = (*it);
+ if(mapping->subframesslots.size() > 2 && fmt->audio.i_channels > 2)
+ ConfigureChannels(fmt->audio.i_channels, &mapping->fmt);
+ mapping->buffer.setSubFramesCount(mapping->fmt.audio.i_channels);
+ return &mapping->fmt;
+ }
+ assert(0);
+ return NULL;
+}
+
SDIAudioMultiplex::SDIAudioMultiplex(uint8_t channels)
{
config = SDIAudioMultiplexConfig(channels);
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.hpp b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
index 7606b3d2e6..a571cf689f 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.hpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
@@ -46,26 +46,36 @@ namespace sdi_sout
SDIAudioMultiplexConfig(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 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> getConfiguredSlots(const StreamID &) const;
+ bool addMapping(const StreamID &, const es_format_t *);
bool addMapping(const StreamID &, std::vector<uint8_t>);
unsigned getMaxSamplesForBlockSize(size_t) const;
private:
+ bool addMapping(const StreamID &, unsigned);
class Mapping
{
public:
Mapping(const StreamID &);
+ ~Mapping();
StreamID id;
+ es_format_t fmt;
SDIAudioMultiplexBuffer buffer;
std::vector<uint8_t> subframesslots;
};
std::vector<Mapping *> mappings;
unsigned subframeslotbitmap;
uint8_t framewidth;
+ bool b_accept_any;
};
class SDIAudioMultiplex
diff --git a/modules/stream_out/sdi/SDIOutput.cpp b/modules/stream_out/sdi/SDIOutput.cpp
index fce8a00d77..f3ee7f7d7a 100644
--- a/modules/stream_out/sdi/SDIOutput.cpp
+++ b/modules/stream_out/sdi/SDIOutput.cpp
@@ -42,11 +42,11 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
p_stream->pace_nocontrol = true;
es_format_Init(&video.configuredfmt, VIDEO_ES, 0);
- es_format_Init(&audio.configuredfmt, AUDIO_ES, 0);
video.tenbits = var_InheritBool(p_stream, CFG_PREFIX "tenbits");
video.nosignal_delay = var_InheritInteger(p_stream, CFG_PREFIX "nosignal-delay");
video.pic_nosignal = NULL;
audio.i_channels = var_InheritInteger(p_stream, CFG_PREFIX "channels");;
+ audio.b_configured = false;
ancillary.afd = var_InheritInteger(p_stream, CFG_PREFIX "afd");
ancillary.ar = var_InheritInteger(p_stream, CFG_PREFIX "ar");
ancillary.afd_line = var_InheritInteger(p_stream, CFG_PREFIX "afd-line");
@@ -54,6 +54,12 @@ SDIOutput::SDIOutput(sout_stream_t *p_stream_)
videoStream = NULL;
captionsStream = NULL;
audioMultiplex = new SDIAudioMultiplex( var_InheritInteger(p_stream, CFG_PREFIX "channels") );
+ char *psz_channelsconf = var_InheritString(p_stream, CFG_PREFIX "audio");
+ if(psz_channelsconf)
+ {
+ audioMultiplex->config.parseConfiguration(VLC_OBJECT(p_stream), psz_channelsconf);
+ free(psz_channelsconf);
+ }
}
SDIOutput::~SDIOutput()
@@ -69,7 +75,6 @@ SDIOutput::~SDIOutput()
if(video.pic_nosignal)
picture_Release(video.pic_nosignal);
es_format_Clean(&video.configuredfmt);
- es_format_Clean(&audio.configuredfmt);
}
AbstractStream *SDIOutput::Add(const es_format_t *fmt)
@@ -88,27 +93,29 @@ AbstractStream *SDIOutput::Add(const es_format_t *fmt)
}
else if(fmt->i_cat == AUDIO_ES && audio.i_channels)
{
- if(audio.configuredfmt.i_codec || ConfigureAudio(&fmt->audio) == VLC_SUCCESS)
+ if(audio.b_configured || 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;
+ const es_format_t *cfgfmt = audioMultiplex->config.getConfigurationForStream(id);
+ if(!cfgfmt)
+ {
+ if(!audioMultiplex->config.addMapping(id, fmt))
+ return NULL;
+ }
+ cfgfmt = audioMultiplex->config.updateFromRealESConfig(id, fmt);
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);
+ 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++)
{
- audioMultiplex->config.setSubFrameSlotUsed(slots[i]);
+ msg_Dbg(p_stream, "%s slot %d to read from channel %zd",
+ id.toString().c_str(), slots[i], i);
audioMultiplex->SetSubFrameSource(slots[i], buffer, AES3AudioSubFrameIndex(i));
}
}
diff --git a/modules/stream_out/sdi/SDIOutput.hpp b/modules/stream_out/sdi/SDIOutput.hpp
index c2fb2d77d4..290f2a315b 100644
--- a/modules/stream_out/sdi/SDIOutput.hpp
+++ b/modules/stream_out/sdi/SDIOutput.hpp
@@ -64,8 +64,8 @@ namespace sdi_sout
struct
{
- es_format_t configuredfmt;
uint8_t i_channels;
+ bool b_configured;
} audio;
struct
diff --git a/modules/stream_out/sdi/sdiout.cpp b/modules/stream_out/sdi/sdiout.cpp
index eb450cfb10..c7b3f88200 100644
--- a/modules/stream_out/sdi/sdiout.cpp
+++ b/modules/stream_out/sdi/sdiout.cpp
@@ -75,6 +75,15 @@
#define VIDEO_TENBITS_LONGTEXT N_(\
"Use 10 bits per pixel for video frames.")
+#define AUDIO_TEXT "Audio channels configuration (Default, single, auto)"
+#define AUDIO_LONGTEXT "Configuration string SEL{CHANS} tokens, ':' separated. " \
+ "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. " \
+ "Use 'only' to accept only declared ES. " \
+ "ex: only:#145{0,1}:#142{2,3}:2{chans=6} "
+
+
/* Video Connections */
static const char *const ppsz_videoconns[] = {
"sdi",
@@ -201,5 +210,6 @@ vlc_module_begin ()
set_section(N_("DeckLink Audio Options"), NULL)
add_integer_with_range(CFG_PREFIX "channels", 2, 0, 16,
CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
+ add_string(CFG_PREFIX "audio", "", AUDIO_TEXT, AUDIO_LONGTEXT, true)
vlc_module_end ()
More information about the vlc-commits
mailing list