/*****************************************************************************
* 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 "sdiout.hpp"
#include "AES3Audio.hpp"
#include <algorithm>
#include <cassert>
using namespace sdi_sout;
AES3AudioBuffer::AES3AudioBuffer(vlc_object_t *p_obj, unsigned count)
{
obj = p_obj;
vlc_mutex_init(&bytestream_mutex);
setSubFramesCount(count);
block_BytestreamInit(&bytestream);
toconsume = 0;
i_codec = VLC_CODEC_S16N;
}
AES3AudioBuffer::~AES3AudioBuffer()
{
block_BytestreamRelease(&bytestream);
}
void AES3AudioBuffer::setSubFramesCount(uint8_t c)
{
buffersubframes = c;
}
void AES3AudioBuffer::push(block_t *p_block)
{
vlc_mutex_locker locker(&bytestream_mutex);
block_BytestreamPush(&bytestream, p_block);
}
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 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
{
vlc_mutex_locker locker(&bytestream_mutex);
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 + 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));
}
}
return 0;
}
size_t AES3AudioBuffer::FramesToBytes(unsigned f) const
{
return (size_t) f * sizeof(uint16_t) * buffersubframes;
}
vlc_tick_t AES3AudioBuffer::FramesToDuration(unsigned f) const
{
return vlc_tick_from_samples(f, 48000);
}
unsigned AES3AudioBuffer::BytesToFrames(size_t s) const
{
return s / (sizeof(uint16_t) * buffersubframes);
}
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)
{
size_t bytes = FramesToBytes(toconsume);
{
vlc_mutex_locker locker(&bytestream_mutex);
if(block_SkipBytes(&bytestream, bytes) == VLC_SUCCESS)
block_BytestreamFlush(&bytestream);
else
block_BytestreamEmpty(&bytestream);
}
#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);
toconsume = f;
}
void AES3AudioBuffer::forwardTo(vlc_tick_t t)
{
if(bufferStart() == VLC_TICK_INVALID || t <= bufferStart())
return;
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;
vlc_mutex_locker locker(&bytestream_mutex);
if(bytestream.p_block)
start = bytestream.p_block->i_pts +
FramesToDuration(BytesToFrames(bytestream.i_block_offset));
return start;
}
unsigned AES3AudioBuffer::availableVirtualSamples(vlc_tick_t from) const
{
vlc_tick_t start = bufferStart();
if(start == VLC_TICK_INVALID)
return 0;
unsigned samples;
{
vlc_mutex_locker locker(&bytestream_mutex);
/* FIXME */
samples = BytesToFrames(block_BytestreamRemaining(&bytestream));
}
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()
{
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();
}
unsigned AES3AudioSubFrameSource::copy(void *buf,
unsigned count,
vlc_tick_t from,
const AES3AudioSubFrameIndex &srcsubframeidx,
unsigned widthinframes)
{
if(aes3AudioBuffer == NULL)
return 0;
return aes3AudioBuffer->read(buf, count, from, srcsubframeidx, bufferSubFrameIdx, widthinframes);
}
void AES3AudioSubFrameSource::flushConsumed()
{
if(aes3AudioBuffer)
aes3AudioBuffer->flushConsumed();
}
void AES3AudioSubFrameSource::tagVirtualConsumed(vlc_tick_t from, unsigned count)
{
if(aes3AudioBuffer)
aes3AudioBuffer->tagVirtualConsumed(from, count);
}
void AES3AudioSubFrameSource::forwardTo(vlc_tick_t t)
{
if(aes3AudioBuffer)
aes3AudioBuffer->forwardTo(t);
}
bool AES3AudioSubFrameSource::available() const
{
return aes3AudioBuffer == NULL;
}
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->availableVirtualSamples(from);
}
unsigned AES3AudioSubFrameSource::alignedInterleaveInSamples(vlc_tick_t from, unsigned n) const
{
if(aes3AudioBuffer == NULL)
return 0;
return aes3AudioBuffer->alignedInterleaveInSamples(from, n);
}
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::availableVirtualSamples(vlc_tick_t from) const
{
if(!subframe0.available() && !subframe1.available())
return std::min(subframe0.availableVirtualSamples(from), subframe1.availableVirtualSamples(from));
else if(subframe1.available())
return subframe0.availableVirtualSamples(from);
else
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()
{
subframe0.flushConsumed();
subframe1.flushConsumed();
}
void AES3AudioFrameSource::tagVirtualConsumed(vlc_tick_t from, unsigned samples)
{
subframe0.tagVirtualConsumed(from, samples);
subframe1.tagVirtualConsumed(from, samples);
}
void AES3AudioFrameSource::forwardTo(vlc_tick_t t)
{
subframe0.forwardTo(t);
subframe1.forwardTo(t);
}
AES3AudioSubFrameIndex::AES3AudioSubFrameIndex(uint8_t v)
{
subframeindex = v;
}
uint8_t AES3AudioSubFrameIndex::index() const
{
return subframeindex;
}
bool AES3AudioSubFrameIndex::isValid() const
{
return subframeindex < MAX_AES3_AUDIO_SUBFRAMES;
}
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|