1 /*****************************************************************************
2 * SDIAudioMultiplex.cpp: SDI Audio Multiplexing
3 *****************************************************************************
4 * Copyright © 2018 VideoLabs, VideoLAN and VideoLAN Authors
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
24 #include "SDIAudioMultiplex.hpp"
30 using namespace sdi_sout
;
32 SDIAudioMultiplexBuffer::SDIAudioMultiplexBuffer(vlc_object_t
*obj
)
33 : AES3AudioBuffer(obj
, 2), AbstractStreamOutputBuffer()
38 SDIAudioMultiplexBuffer::~SDIAudioMultiplexBuffer()
43 void SDIAudioMultiplexBuffer::FlushQueued()
48 void SDIAudioMultiplexBuffer::Enqueue(void *p
)
50 AES3AudioBuffer::push(reinterpret_cast<block_t
*>(p
));
53 void * SDIAudioMultiplexBuffer::Dequeue()
58 static void ConfigureChannels(unsigned i
, es_format_t
*fmt
)
63 fmt
->audio
.i_physical_channels
= AOUT_CHANS_7_1
;
68 fmt
->audio
.i_physical_channels
= AOUT_CHANS_5_1
;
72 fmt
->audio
.i_physical_channels
= AOUT_CHANS_STEREO
;
74 fmt
->audio
.i_channels
= i
;
75 fmt
->audio
.i_blockalign
= i
* 16 / 8;
78 SDIAudioMultiplexConfig::Mapping::Mapping(vlc_object_t
*obj
, const StreamID
&id
)
81 es_format_Init(&fmt
, AUDIO_ES
, VLC_CODEC_S16N
);
82 fmt
.audio
.i_format
= VLC_CODEC_S16N
;
83 fmt
.audio
.i_rate
= 48000;
84 fmt
.audio
.i_bitspersample
= 16;
85 ConfigureChannels(2, &fmt
);
89 SDIAudioMultiplexConfig::Mapping::~Mapping()
91 es_format_Clean(&fmt
);
94 SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(vlc_object_t
*obj
, uint8_t channels
)
97 subframeslotbitmap
= 0;
100 else if(channels
> 2)
107 SDIAudioMultiplexConfig::~SDIAudioMultiplexConfig()
109 for(size_t i
=0; i
<mappings
.size(); i
++)
113 bool SDIAudioMultiplexConfig::decode(const StreamID
&id
) const
115 const Mapping
*map
= getMappingByID(id
);
117 return map
->b_decode
;
121 bool SDIAudioMultiplexConfig::SubFrameSlotUsed(uint8_t i
) const
123 return (1 << i
) & subframeslotbitmap
;
126 void SDIAudioMultiplexConfig::setSubFrameSlotUsed(uint8_t i
)
128 subframeslotbitmap
|= (1 << i
);
131 void SDIAudioMultiplexConfig::parseConfiguration(vlc_object_t
*obj
, const char *psz
)
134 char *psz_in
= (char*)psz
;
135 config_chain_t
*p_config_chain
= NULL
;
138 char *psz_next
= config_ChainCreate(&name
, &p_config_chain
, psz_in
);
141 if(!std::strcmp(name
, "only"))
143 b_accept_any
= false;
144 msg_Dbg(obj
, "only accepting declared streams");
146 else /* try mapping decl */
150 int *pi_id
= &i_seqid
;
151 const char *psz_id
= name
;
160 int i_val
= std::strtol(psz_id
, &end
, 10);
161 if(end
!= NULL
&& *end
== '\0')
164 if(i_id
!= -1 || i_seqid
!= -1)
166 msg_Dbg(obj
,"found declaration for ES %s %d",
167 (i_id
> -1) ? "pid #" : "seq", *pi_id
);
168 bool b_embed
= false;
169 int i_reserved_chans
= 0;
170 std::vector
<uint8_t> subframeslots
;
171 for(config_chain_t
*p
= p_config_chain
; p
; p
= p
->p_next
)
173 if(!std::strcmp("embed", p
->psz_name
))
176 msg_Dbg(obj
," * mode passthrough set");
178 else if(!std::strcmp("chans", p
->psz_name
) && subframeslots
.empty())
181 int i_val
= std::strtol(p
->psz_value
, &end
, 10);
182 if(end
!= NULL
&& *end
== '\0')
184 i_reserved_chans
= i_val
;
185 msg_Dbg(obj
," * provisioned %d channels", i_val
);
187 else msg_Warn(obj
, " * ignoring channels count declaration %d", i_val
);
189 else if(i_reserved_chans
== 0)
192 int i_slot
= std::strtol(p
->psz_name
, &end
, 10);
193 if(end
!= NULL
&& *end
== '\0')
195 if(i_slot
< MAX_AES3_AUDIO_SUBFRAMES
&& i_slot
< (2 * framewidth
) &&
196 std::find(subframeslots
.begin(), subframeslots
.end(), i_slot
) == subframeslots
.end())
198 subframeslots
.push_back(i_slot
);
199 msg_Dbg(obj
," * mapped channel %zd to subframe %d",
200 subframeslots
.size(), i_slot
);
202 else msg_Warn(obj
, " * ignoring invalid subframe declaration %d", i_slot
);
204 else msg_Warn(obj
, " * ignoring unknown/invalid token %s", p
->psz_name
);
208 bool b_success
= false;
210 b_success
= addMappingEmbed(StreamID(i_id
, i_seqid
));
211 else if(subframeslots
.empty() && i_reserved_chans
)
212 b_success
= addMapping(StreamID(i_id
, i_seqid
), i_reserved_chans
);
213 else if(!subframeslots
.empty())
214 b_success
= addMapping(StreamID(i_id
, i_seqid
), subframeslots
);
217 msg_Dbg(obj
, " * successfully configured");
219 msg_Warn(obj
, " * configuration rejected (duplicate or not enough subframes ?)");
224 config_ChainDestroy(p_config_chain
);
231 std::vector
<uint8_t> SDIAudioMultiplexConfig::getFreeSubFrameSlots(bool b_aligned
) const
233 std::vector
<uint8_t> slots
;
234 for(uint8_t i
=0; i
<getMultiplexedFramesCount() * 2; i
++)
236 if(!SubFrameSlotUsed(i
))
240 for( ; b_aligned
&& slots
.size() >= 2; slots
.erase(slots
.begin()))
242 /* get aligned subframes pair */
243 if((slots
[0] & 1) == 0 && slots
[1] == slots
[0] + 1)
250 std::vector
<uint8_t> SDIAudioMultiplexConfig::getConfiguredSlots(const StreamID
&id
) const
252 for(size_t i
=0; i
<mappings
.size(); i
++)
254 if(mappings
[i
]->id
== id
)
255 return mappings
[i
]->subframesslots
;
257 return std::vector
<uint8_t>();
260 bool SDIAudioMultiplexConfig::addMapping(const StreamID
&id
, const es_format_t
*fmt
)
262 if(!fmt
->audio
.i_channels
|| !b_accept_any
)
264 return addMapping(id
, fmt
->audio
.i_channels
);
267 bool SDIAudioMultiplexConfig::addMapping(const StreamID
&id
, unsigned channels
)
269 std::vector
<uint8_t> slots
= getFreeSubFrameSlots();
270 if(slots
.size() < channels
)
272 slots
.resize(channels
);
273 return addMapping(id
, slots
);
276 bool SDIAudioMultiplexConfig::addMappingEmbed(const StreamID
&id
, std::vector
<uint8_t> slots
)
279 slots
= getFreeSubFrameSlots(true);
283 bool b
= addMapping(id
, slots
);
285 getMappingByID(id
)->b_decode
= false;
289 bool SDIAudioMultiplexConfig::addMapping(const StreamID
&id
, std::vector
<uint8_t> subframeslots
)
291 for(size_t i
=0; i
<mappings
.size(); i
++)
292 if(mappings
[i
]->id
== id
)
294 for(size_t i
=0; i
<subframeslots
.size(); i
++)
295 if(SubFrameSlotUsed(subframeslots
[i
]))
298 Mapping
*assoc
= new Mapping(obj
, id
);
299 assoc
->subframesslots
= subframeslots
;
301 mappings
.push_back(assoc
);
303 for(size_t i
=0; i
<subframeslots
.size(); i
++)
304 setSubFrameSlotUsed(subframeslots
[i
]);
309 SDIAudioMultiplexConfig::Mapping
*
310 SDIAudioMultiplexConfig::getMappingByID(const StreamID
&id
)
312 auto it
= std::find_if(mappings
.begin(), mappings
.end(),
313 [&id
](Mapping
*e
) { return e
->id
== id
; });
314 return (it
!= mappings
.end()) ? *it
: NULL
;
317 const SDIAudioMultiplexConfig::Mapping
*
318 SDIAudioMultiplexConfig::getMappingByID(const StreamID
&id
) const
320 auto it
= std::find_if(mappings
.begin(), mappings
.end(),
321 [&id
](const Mapping
*e
) { return e
->id
== id
; });
322 return (it
!= mappings
.end()) ? *it
: NULL
;
325 unsigned SDIAudioMultiplexConfig::getMaxSamplesForBlockSize(size_t s
) const
327 return s
/ (2 * sizeof(uint16_t) * getMultiplexedFramesCount());
330 SDIAudioMultiplexBuffer
*
331 SDIAudioMultiplexConfig::getBufferForStream(const StreamID
&id
)
333 Mapping
*map
= getMappingByID(id
);
334 return map
? &map
->buffer
: NULL
;
337 const es_format_t
* SDIAudioMultiplexConfig::getConfigurationForStream(const StreamID
&id
) const
339 const Mapping
*map
= getMappingByID(id
);
340 return map
? &map
->fmt
: NULL
;
344 SDIAudioMultiplexConfig::updateFromRealESConfig(const StreamID
&id
,
345 const es_format_t
*fmt
)
347 Mapping
*mapping
= getMappingByID(id
);
350 if(mapping
->subframesslots
.size() > 2 && fmt
->audio
.i_channels
> 2)
351 ConfigureChannels(fmt
->audio
.i_channels
, &mapping
->fmt
);
352 mapping
->buffer
.setSubFramesCount(mapping
->fmt
.audio
.i_channels
);
353 return &mapping
->fmt
;
359 SDIAudioMultiplex::SDIAudioMultiplex(vlc_object_t
*obj
, uint8_t channels
)
360 : config(SDIAudioMultiplexConfig(obj
, channels
))
363 head
= VLC_TICK_INVALID
;
366 SDIAudioMultiplex::~SDIAudioMultiplex()
371 unsigned SDIAudioMultiplex::availableVirtualSamples(vlc_tick_t from
) const
373 unsigned samples
= std::numeric_limits
<unsigned>::max();
374 for(size_t i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
376 if(framesources
[i
].subframe0
.available() &&
377 framesources
[i
].subframe1
.available())
379 samples
= std::min(samples
, framesources
[i
].availableVirtualSamples(from
));
381 return samples
< std::numeric_limits
<unsigned>::max() ? samples
: 0;
384 unsigned SDIAudioMultiplex::alignedInterleaveInSamples(vlc_tick_t from
, unsigned i_wanted
) const
386 unsigned i_align
= i_wanted
;
387 for(size_t i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
389 if(!framesources
[i
].subframe0
.available())
390 i_align
= std::min(i_align
, framesources
[i
].subframe0
.alignedInterleaveInSamples(from
, i_wanted
));
391 if(!framesources
[i
].subframe1
.available())
392 i_align
= std::min(i_align
, framesources
[i
].subframe1
.alignedInterleaveInSamples(from
, i_wanted
));
397 vlc_tick_t
SDIAudioMultiplex::bufferStart() const
399 vlc_tick_t start
= VLC_TICK_INVALID
;
400 for(size_t i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
402 if(framesources
[i
].subframe0
.available() &&
403 framesources
[i
].subframe1
.available())
405 vlc_tick_t t
= framesources
[i
].bufferStartTime();
406 if(start
== VLC_TICK_INVALID
||
407 (t
!= VLC_TICK_INVALID
&& t
<start
))
413 unsigned SDIAudioMultiplex::getFreeSubFrameSlots() const
415 unsigned bitfield
= 0;
416 for(unsigned i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
418 const AES3AudioFrameSource
*source
= &framesources
[i
];
419 if(source
->subframe0
.available())
420 bitfield
|= (1 << (i
* 2 + 0));
421 if(source
->subframe1
.available())
422 bitfield
|= (1 << (i
* 2 + 1));
427 void SDIAudioMultiplex::SetSubFrameSource(uint8_t n
, AES3AudioBuffer
*buf
,
428 AES3AudioSubFrameIndex idx
)
430 assert(n
<MAX_AES3_AUDIO_SUBFRAMES
);
431 AES3AudioFrameSource
*f
= &framesources
[n
/ 2];
432 AES3AudioSubFrameSource
*s
= (n
& 1) ? &f
->subframe1
: &f
->subframe0
;
433 assert(s
->available());
434 *s
= AES3AudioSubFrameSource(buf
, idx
);
437 #ifdef SDI_MULTIPLEX_DEBUG
438 void SDIAudioMultiplex::Debug() const
440 msg_Dbg(p_obj
, "Multiplex: head %ld bufferstart() %ld", head
, bufferStart());
441 for(unsigned i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
443 const AES3AudioFrameSource
*source
= &framesources
[i
];
444 if(!source
->subframe0
.available())
445 msg_Dbg(p_obj
, " [%d.0] bufferstart() %ld", i
, source
->subframe0
.bufferStartTime());
446 if(!source
->subframe1
.available())
447 msg_Dbg(p_obj
, " [%d.1] bufferstart() %ld", i
, source
->subframe1
.bufferStartTime());
452 block_t
* SDIAudioMultiplex::Extract(unsigned samples
)
454 vlc_tick_t start
= bufferStart();
456 uint8_t interleavedframes
= config
.getMultiplexedFramesCount();
458 /* Ensure we never roll back due to late fifo */
459 if(head
!= VLC_TICK_INVALID
)
463 for(unsigned i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
464 framesources
[i
].forwardTo(head
);
469 block_t
*p_block
= block_Alloc( interleavedframes
* 2 * sizeof(uint16_t) * samples
);
472 memset(p_block
->p_buffer
, 0, p_block
->i_buffer
);
474 p_block
->i_pts
= p_block
->i_dts
= start
;
475 p_block
->i_nb_samples
= samples
;
477 for(unsigned i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
479 AES3AudioFrameSource
*source
= &framesources
[i
];
480 unsigned ahead
= source
->availableVirtualSamples(start
);
484 #ifdef SDI_MULTIPLEX_DEBUG
485 vlc_fourcc_t i_codec
= source
->subframe0
.getCodec();
486 msg_Dbg(p_obj
, "%4.4s pair %u tocopy %u from %ld head %ld, avail %u",
487 reinterpret_cast<const char *>(&i_codec
), i
, samples
,
488 start
, source
->bufferStartTime(), ahead
);
491 source
->subframe0
.copy(p_block
->p_buffer
, samples
, start
, (i
* 2 + 0), interleavedframes
);
492 source
->subframe1
.copy(p_block
->p_buffer
, samples
, start
, (i
* 2 + 1), interleavedframes
);
496 for(unsigned i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
497 framesources
[i
].tagVirtualConsumed(start
, samples
);
498 for(unsigned i
=0; i
<MAX_AES3_AUDIO_FRAMES
; i
++)
499 framesources
[i
].flushConsumed();
501 head
= bufferStart();