Skip to content

Commit

Permalink
Expand Audio Framework to only ever decode S16, interleaved audio unl…
Browse files Browse the repository at this point in the history
…ess explicitly configured otherwise

This will greatly simplify custom audio class like the one used in mythtranscode.
Add two methods to AudioOutput:
-CanProcess: will tell if the audio instance can handle a given sample format
-DecodeAudio: will decode audio and de-interleave according to what the audio instance can receive

AudioPlayer is extended accordingly. AVFD will now use new methods to decode audio before feeding it to the audio class.
  • Loading branch information
jyavenard committed Jun 19, 2013
1 parent ef80f72 commit f0d803b
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 15 deletions.
29 changes: 28 additions & 1 deletion mythtv/libs/libmyth/audio/audioconvert.cpp
Expand Up @@ -631,13 +631,19 @@ AudioConvert::~AudioConvert()
* Consumes 'bytes' bytes from in and returns the numer of bytes written to out
* return negative number if error
*/
int AudioConvert::Process(void* out, const void* in, int bytes)
int AudioConvert::Process(void* out, const void* in, int bytes, bool noclip)
{
if (bytes <= 0)
return 0;
if (m_out == FORMAT_NONE || m_in == FORMAT_NONE)
return 0;

if (noclip && m_in == m_out)
{
memcpy(out, in, bytes);
return bytes;
}

/* use conversion routines to perform clipping on samples */
if (m_in == FORMAT_FLT)
return fromFloat(m_out, out, in, bytes);
Expand Down Expand Up @@ -833,3 +839,24 @@ void AudioConvert::InterleaveSamples(AudioFormat format, int channels,
_InterleaveSample((int*)output, (const int*)input, channels, data_size/sizeof(int)/channels);
}
}

void AudioConvert::DeinterleaveSamples(int channels,
uint8_t* output, const uint8_t* input,
int data_size)
{
DeinterleaveSamples(m_in, channels, output, input, data_size);
}

void AudioConvert::InterleaveSamples(int channels,
uint8_t* output, const uint8_t* const* input,
int data_size)
{
InterleaveSamples(m_in, channels, output, input, data_size);
}

void AudioConvert::InterleaveSamples(int channels,
uint8_t* output, const uint8_t* input,
int data_size)
{
InterleaveSamples(m_in, channels, output, input, data_size);
}
14 changes: 13 additions & 1 deletion mythtv/libs/libmyth/audio/audioconvert.h
Expand Up @@ -44,13 +44,25 @@ class MPUBLIC AudioConvert
*
* Return Value: size in bytes of samples converted or <= 0 if error
*/
int Process(void* out, const void* in, int bytes);
int Process(void* out, const void* in, int bytes, bool noclip = false);
AudioFormat Out(void) { return m_out; }
AudioFormat In(void) { return m_in; }

bool operator==(AudioConvert& rhs) const
{ return m_in == rhs.m_in && m_out == rhs.m_out; }
bool operator!=(AudioConvert& rhs) const
{ return m_in != m_out; }

void DeinterleaveSamples(int channels,
uint8_t* output, const uint8_t* input,
int data_size);
void InterleaveSamples(int channels,
uint8_t* output, const uint8_t* const* input,
int data_size);
void InterleaveSamples(int channels,
uint8_t* output, const uint8_t* input,
int data_size);

// static utilities
static int toFloat(AudioFormat format, void* out, const void* in, int bytes);
static int fromFloat(AudioFormat format, void* out, const void* in, int bytes);
Expand Down
95 changes: 94 additions & 1 deletion mythtv/libs/libmyth/audio/audiooutput.cpp
Expand Up @@ -37,6 +37,13 @@ using namespace std;
#include "audiopulsehandler.h"
#endif

extern "C" {
#include "libavcodec/avcodec.h" // to get codec id
}
#include "audioconvert.h"

#define LOC QString("AO: ")

void AudioOutput::Cleanup(void)
{
#ifdef USING_PULSE
Expand Down Expand Up @@ -325,7 +332,7 @@ AudioOutput::AudioDeviceConfig* AudioOutput::GetAudioDeviceConfig(
// We have an ELD, show actual reported capabilities
capabilities += " (" + aosettings.getELD().codecs_desc() + ")";
}
else
else
{
// build capabilities string, in a similar fashion as reported
// by ELD
Expand Down Expand Up @@ -531,3 +538,89 @@ AudioOutput::ADCVect* AudioOutput::GetOutputList(void)
}
return list;
}

/**
* DecodeAudio
* Decode an audio packet, and compact it if data is planar
* Return negative error code if an error occurred during decoding
* or the number of bytes consumed from the input AVPacket
* data_size contains the size of decoded data copied into buffer
*/
int AudioOutput::DecodeAudio(AVCodecContext *ctx,
uint8_t *buffer, int &data_size,
const AVPacket *pkt)
{
AVFrame frame;
int got_frame = 0;
int ret;
char error[AV_ERROR_MAX_STRING_SIZE];

data_size = 0;
avcodec_get_frame_defaults(&frame);
ret = avcodec_decode_audio4(ctx, &frame, &got_frame, pkt);
if (ret < 0)
{
LOG(VB_AUDIO, LOG_ERR, LOC +
QString("audio decode error: %1 (%2)")
.arg(av_make_error_string(error, sizeof(error), ret))
.arg(got_frame));
return ret;
}

if (!got_frame)
{
LOG(VB_AUDIO, LOG_DEBUG, LOC +
QString("audio decode, no frame decoded (%1)").arg(ret));
return ret;
}

AVSampleFormat format = (AVSampleFormat)frame.format;
AudioFormat fmt =
AudioOutputSettings::AVSampleFormatToFormat(format, ctx->bits_per_raw_sample);

data_size = frame.nb_samples * frame.channels * av_get_bytes_per_sample(format);

// May need to convert audio to S16
AudioConvert converter(fmt, CanProcess(fmt) ? fmt : FORMAT_S16);
uint8_t* src;

if (av_sample_fmt_is_planar(format))
{
src = buffer;
converter.InterleaveSamples(frame.channels,
src,
(const uint8_t **)frame.extended_data,
data_size);
}
else
{
// data is already compacted...
src = frame.extended_data[0];
}

uint8_t* transit = buffer;

if (!CanProcess(fmt) &&
av_get_bytes_per_sample(ctx->sample_fmt) < AudioOutputSettings::SampleSize(converter.Out()))
{
// this conversion can't be done in place
transit = (uint8_t*)av_malloc(data_size * av_get_bytes_per_sample(ctx->sample_fmt)
/ AudioOutputSettings::SampleSize(converter.Out()));
if (!transit)
{
LOG(VB_AUDIO, LOG_ERR, LOC +
QString("audio decode, out of memory"));
data_size = 0;
return ret;
}
}
if (!CanProcess(fmt) || src != transit)
{
data_size = converter.Process(transit, src, data_size, true);
}
if (transit != buffer)
{
av_free(transit);
}
return ret;
}
35 changes: 33 additions & 2 deletions mythtv/libs/libmyth/audio/audiooutput.h
Expand Up @@ -11,6 +11,10 @@
#include "volumebase.h"
#include "output.h"

// forward declaration
struct AVCodecContext;
struct AVPacket;

class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners
{
public:
Expand Down Expand Up @@ -79,12 +83,12 @@ class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners
/**
* AddData:
* Add data to the audiobuffer for playback
*
*
* in:
* buffer : pointer to audio data
* len : length of audio data added
* timecode: timecode of the first sample added
* frames : number of frames added.
* frames : number of frames added.
* out:
* return false if there wasn't enough space in audio buffer to
* process all the data
Expand Down Expand Up @@ -136,6 +140,33 @@ class MPUBLIC AudioOutput : public VolumeBase, public OutputListeners
virtual bool ToggleUpmix(void) { return false; }
virtual bool CanUpmix(void) { return false; }
bool PulseStatus(void) { return pulsewassuspended; }
/**
* CanProcess
* argument: AudioFormat
* return true if class can handle AudioFormat
* All AudioOutput derivative must be able to handle S16
*/
virtual bool CanProcess(AudioFormat fmt) { return fmt == FORMAT_S16; }
/**
* CanProcess
* return bitmask of all AudioFormat handled
* All AudioOutput derivative must be able to handle S16
*/
virtual uint32_t CanProcess(void) { return 1 << FORMAT_S16; }

/**
* DecodeAudio
* Utility routine.
* Decode an audio packet, and compact it if data is planar
* Return negative error code if an error occurred during decoding
* or the number of bytes consumed from the input AVPacket
* data_size contains the size of decoded data copied into buffer
* data decoded will be S16 samples if class instance can't handle HD audio
* or S16 and above otherwise. No U8 PCM format can be returned
*/
int DecodeAudio(AVCodecContext *ctx,
uint8_t *buffer, int &data_size,
const AVPacket *pkt);

protected:
void Error(const QString &msg);
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmyth/audio/audiooutputbase.cpp
Expand Up @@ -20,7 +20,7 @@
#include "spdifencoder.h"
#include "mythlogging.h"

#define LOC QString("AO: ")
#define LOC QString("AOBase: ")

#define WPOS audiobuffer + org_waud
#define RPOS audiobuffer + raud
Expand Down
6 changes: 6 additions & 0 deletions mythtv/libs/libmyth/audio/audiooutputbase.h
Expand Up @@ -80,6 +80,12 @@ class AudioOutputBase : public AudioOutput, public MThread
virtual bool IsUpmixing(void);
virtual bool ToggleUpmix(void);
virtual bool CanUpmix(void);
virtual bool CanProcess(AudioFormat fmt) { return true; }
virtual uint32_t CanProcess(void)
{
// we support all codec
return ~(((~0ULL) >> FORMAT_FLT) << FORMAT_FLT);
}

virtual void Reset(void);

Expand Down
41 changes: 40 additions & 1 deletion mythtv/libs/libmythtv/audioplayer.cpp
Expand Up @@ -262,7 +262,7 @@ void AudioPlayer::SetAudioParams(AudioFormat format, int orig_channels,
int samplerate, bool passthru,
int codec_profile)
{
m_format = format;
m_format = CanProcess(format) ? format : FORMAT_S16;
m_orig_channels = orig_channels;
m_channels = channels;
m_codec = codec;
Expand Down Expand Up @@ -516,3 +516,42 @@ bool AudioPlayer::IsBufferAlmostFull(void)
}
return false;
}

bool AudioPlayer::CanProcess(AudioFormat fmt)
{
if (!m_audioOutput)
return false;
else
return m_audioOutput->CanProcess(fmt);
}

uint32_t AudioPlayer::CanProcess(void)
{
if (!m_audioOutput)
return 0;
else
return m_audioOutput->CanProcess();
}

/**
* DecodeAudio
* Utility routine.
* Decode an audio packet, and compact it if data is planar
* Return negative error code if an error occurred during decoding
* or the number of bytes consumed from the input AVPacket
* data_size contains the size of decoded data copied into buffer
* data decoded will be S16 samples if class instance can't handle HD audio
* or S16 and above otherwise. No U8 PCM format can be returned
*/
int AudioPlayer::DecodeAudio(AVCodecContext *ctx,
uint8_t *buffer, int &data_size,
const AVPacket *pkt)
{
if (!m_audioOutput)
{
data_size = 0;
return 0;
}
else
return m_audioOutput->DecodeAudio(ctx, buffer, data_size, pkt);
}
12 changes: 10 additions & 2 deletions mythtv/libs/libmythtv/audioplayer.h
Expand Up @@ -4,8 +4,10 @@
#include <stdint.h>
#include "audiooutputsettings.h"

class MythPlayer;
class AudioOutput;
class MythPlayer;
class AudioOutput;
struct AVCodecContext;
struct AVPacket;

namespace MythTV
{
Expand Down Expand Up @@ -65,6 +67,12 @@ class MTV_PUBLIC AudioPlayer
uint GetMaxChannels(void);
int GetMaxHDRate(void);
int64_t GetAudioTime(void);
AudioFormat GetFormat(void) const { return m_format; }
bool CanProcess(AudioFormat fmt);
uint32_t CanProcess(void);
int DecodeAudio(AVCodecContext *ctx,
uint8_t *buffer, int &data_size,
const AVPacket *pkt);

bool IsMuted(void) { return GetMuteState() == kMuteAll; }
bool SetMuted(bool mute);
Expand Down
12 changes: 6 additions & 6 deletions mythtv/libs/libmythtv/avformatdecoder.cpp
Expand Up @@ -4374,7 +4374,7 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,
ctx->channels = m_audio->GetMaxChannels();
}

ret = AudioOutputUtil::DecodeAudio(ctx, audioSamples, data_size, &tmp_pkt);
ret = m_audio->DecodeAudio(ctx, audioSamples, data_size, &tmp_pkt);
decoded_size = data_size;
already_decoded = true;
reselectAudioTrack |= ctx->channels;
Expand Down Expand Up @@ -4425,7 +4425,7 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,
{
if (m_audio->NeedDecodingBeforePassthrough())
{
ret = AudioOutputUtil::DecodeAudio(ctx, audioSamples, data_size, &tmp_pkt);
ret = m_audio->DecodeAudio(ctx, audioSamples, data_size, &tmp_pkt);
decoded_size = data_size;
}
else
Expand All @@ -4449,7 +4449,7 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,
else
ctx->request_channels = 0;

ret = AudioOutputUtil::DecodeAudio(ctx, audioSamples, data_size, &tmp_pkt);
ret = m_audio->DecodeAudio(ctx, audioSamples, data_size, &tmp_pkt);
decoded_size = data_size;
}

Expand Down Expand Up @@ -4487,9 +4487,9 @@ bool AvFormatDecoder::ProcessAudioPacket(AVStream *curstream, AVPacket *pkt,
extract_mono_channel(audSubIdx, &audioOut,
(char *)audioSamples, data_size);

int frames = (ctx->channels <= 0 || decoded_size < 0) ? -1 :
decoded_size / (ctx->channels *
av_get_bytes_per_sample(ctx->sample_fmt));
int samplesize = AudioOutputSettings::SampleSize(m_audio->GetFormat());
int frames = (ctx->channels <= 0 || decoded_size < 0 || !samplesize) ? -1 :
decoded_size / (ctx->channels * samplesize);
m_audio->AddAudioData((char *)audioSamples, data_size, temppts, frames);
if (audioOut.do_passthru && !m_audio->NeedDecodingBeforePassthrough())
{
Expand Down

0 comments on commit f0d803b

Please sign in to comment.