demux: heif: send extradata with avif
[vlc.git] / modules / stream_out / sdi / SDIAudioMultiplex.cpp
bloba156cf735f08296eed3c904523084a5c881acb73
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 *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include "SDIAudioMultiplex.hpp"
25 #include <vlc_es.h>
26 #include <limits>
27 #include <cstring>
28 #include <algorithm>
30 using namespace sdi_sout;
32 SDIAudioMultiplexBuffer::SDIAudioMultiplexBuffer(vlc_object_t *obj)
33 : AES3AudioBuffer(obj, 2), AbstractStreamOutputBuffer()
38 SDIAudioMultiplexBuffer::~SDIAudioMultiplexBuffer()
40 FlushQueued();
43 void SDIAudioMultiplexBuffer::FlushQueued()
48 void SDIAudioMultiplexBuffer::Enqueue(void *p)
50 AES3AudioBuffer::push(reinterpret_cast<block_t *>(p));
53 void * SDIAudioMultiplexBuffer::Dequeue()
55 return NULL;
58 static void ConfigureChannels(unsigned i, es_format_t *fmt)
60 if( i>=8 )
62 i = 8;
63 fmt->audio.i_physical_channels = AOUT_CHANS_7_1;
65 else if( i>2 )
67 i = 6;
68 fmt->audio.i_physical_channels = AOUT_CHANS_5_1;
70 else
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)
79 : id(id), buffer(obj)
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);
86 b_decode = true;
89 SDIAudioMultiplexConfig::Mapping::~Mapping()
91 es_format_Clean(&fmt);
94 SDIAudioMultiplexConfig::SDIAudioMultiplexConfig(vlc_object_t *obj, uint8_t channels)
96 this->obj = obj;
97 subframeslotbitmap = 0;
98 if(channels > 8)
99 framewidth = 8;
100 else if(channels > 2)
101 framewidth = 4;
102 else
103 framewidth = 1;
104 b_accept_any = true;
107 SDIAudioMultiplexConfig::~SDIAudioMultiplexConfig()
109 for(size_t i=0; i<mappings.size(); i++)
110 delete mappings[i];
113 bool SDIAudioMultiplexConfig::decode(const StreamID &id) const
115 const Mapping *map = getMappingByID(id);
116 if(map)
117 return map->b_decode;
118 return true;
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)
133 char *name = NULL;
134 char *psz_in = (char*)psz;
135 config_chain_t *p_config_chain = NULL;
136 while(psz_in)
138 char *psz_next = config_ChainCreate(&name, &p_config_chain, psz_in);
139 if(name)
141 if(!std::strcmp(name, "only"))
143 b_accept_any = false;
144 msg_Dbg(obj, "only accepting declared streams");
146 else /* try mapping decl */
148 int i_id = -1;
149 int i_seqid = -1;
150 int *pi_id = &i_seqid;
151 const char *psz_id = name;
152 if(psz_id[0]=='#')
154 psz_id++;
155 pi_id = &i_id;
157 if(*psz_id)
159 char *end = NULL;
160 int i_val = std::strtol(psz_id, &end, 10);
161 if(end != NULL && *end == '\0')
162 *pi_id = i_val;
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))
175 b_embed = true;
176 msg_Dbg(obj," * mode passthrough set");
178 else if(!std::strcmp("chans", p->psz_name) && subframeslots.empty())
180 char *end = NULL;
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)
191 char *end = NULL;
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;
209 if(b_embed)
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);
216 if(b_success)
217 msg_Dbg(obj, " * successfully configured");
218 else
219 msg_Warn(obj, " * configuration rejected (duplicate or not enough subframes ?)");
222 free(name);
224 config_ChainDestroy(p_config_chain);
225 if(psz != psz_in)
226 free(psz_in);
227 psz_in = psz_next;
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))
237 slots.push_back(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)
244 break;
247 return slots;
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)
263 return false;
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)
271 return false;
272 slots.resize(channels);
273 return addMapping(id, slots);
276 bool SDIAudioMultiplexConfig::addMappingEmbed(const StreamID &id, std::vector<uint8_t> slots)
278 if(slots.empty())
279 slots = getFreeSubFrameSlots(true);
280 if(slots.size() < 2)
281 return false;
282 slots.resize(2);
283 bool b = addMapping(id, slots);
284 if(b)
285 getMappingByID(id)->b_decode = false;
286 return b;
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)
293 return false;
294 for(size_t i=0; i<subframeslots.size(); i++)
295 if(SubFrameSlotUsed(subframeslots[i]))
296 return false;
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]);
306 return true;
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;
343 const es_format_t *
344 SDIAudioMultiplexConfig::updateFromRealESConfig(const StreamID &id,
345 const es_format_t *fmt)
347 Mapping *mapping = getMappingByID(id);
348 if(mapping)
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;
355 assert(0);
356 return NULL;
359 SDIAudioMultiplex::SDIAudioMultiplex(vlc_object_t *obj, uint8_t channels)
360 : config(SDIAudioMultiplexConfig(obj, channels))
362 p_obj = obj;
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())
378 continue;
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));
394 return i_align;
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())
404 continue;
405 vlc_tick_t t = framesources[i].bufferStartTime();
406 if(start == VLC_TICK_INVALID ||
407 (t != VLC_TICK_INVALID && t<start))
408 start = t;
410 return 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));
424 return bitfield;
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());
450 #endif
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)
461 if(start < head)
463 for(unsigned i=0; i<MAX_AES3_AUDIO_FRAMES; i++)
464 framesources[i].forwardTo(head);
466 start = head;
469 block_t *p_block = block_Alloc( interleavedframes * 2 * sizeof(uint16_t) * samples );
470 if(!p_block)
471 return NULL;
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);
481 if(ahead == 0)
482 continue;
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);
489 #endif
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();
503 return p_block;