Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio playback improvement #268

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DllAvCodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class DllAvCodecInterface
virtual void avcodec_flush_buffers(AVCodecContext *avctx)=0;
virtual int avcodec_open2_dont_call(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)=0;
virtual AVCodec *avcodec_find_decoder(enum AVCodecID id)=0;
virtual AVCodec *avcodec_find_decoder_by_name(const char *name)=0;
virtual AVCodec *avcodec_find_encoder(enum AVCodecID id)=0;
virtual int avcodec_close_dont_call(AVCodecContext *avctx)=0;
virtual AVFrame *av_frame_alloc(void)=0;
Expand Down Expand Up @@ -125,6 +126,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
virtual int avcodec_open2_dont_call(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options) { *(int *)0x0 = 0; return 0; }
virtual int avcodec_close_dont_call(AVCodecContext *avctx) { *(int *)0x0 = 0; return 0; }
virtual AVCodec *avcodec_find_decoder(enum AVCodecID id) { return ::avcodec_find_decoder(id); }
virtual AVCodec *avcodec_find_decoder_by_name(const char *name) { return ::avcodec_find_decoder_by_name(name); }
virtual AVCodec *avcodec_find_encoder(enum AVCodecID id) { return ::avcodec_find_encoder(id); }
virtual int avcodec_close(AVCodecContext *avctx)
{
Expand Down Expand Up @@ -195,6 +197,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface

DEFINE_METHOD0(void, avcodec_register_all_dont_call)
DEFINE_METHOD1(AVCodec*, avcodec_find_decoder, (enum AVCodecID p1))
DEFINE_METHOD1(AVCodec*, avcodec_find_decoder_by_name, (const char *p1))
DEFINE_METHOD1(AVCodec*, avcodec_find_encoder, (enum AVCodecID p1))
DEFINE_METHOD1(int, avcodec_close_dont_call, (AVCodecContext *p1))
DEFINE_METHOD0(AVFrame*, av_frame_alloc)
Expand All @@ -218,6 +221,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
RESOLVE_METHOD_RENAME(avcodec_open2,avcodec_open2_dont_call)
RESOLVE_METHOD_RENAME(avcodec_close,avcodec_close_dont_call)
RESOLVE_METHOD(avcodec_find_decoder)
RESOLVE_METHOD(avcodec_find_decoder_by_name)
RESOLVE_METHOD(avcodec_find_encoder)
RESOLVE_METHOD(av_frame_alloc)
RESOLVE_METHOD_RENAME(avcodec_register_all, avcodec_register_all_dont_call)
Expand Down
227 changes: 226 additions & 1 deletion OMXAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,34 @@ bool COMXAudio::PortSettingsChanged()

if (m_settings_changed)
{
/* setup mixer input */
OMX_INIT_STRUCTURE(m_pcm_output);
m_pcm_output.nPortIndex = m_omx_decoder.GetOutputPort();
omx_err = m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - error m_omx_decoder GetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
}

//// decoder output -> mixer input
memcpy(m_pcm_output.eChannelMapping, m_input_channels, sizeof(m_input_channels));
// round up to power of 2
m_pcm_output.nChannels = m_InputChannels > 4 ? 8 : m_InputChannels > 2 ? 4 : m_InputChannels;
/* limit samplerate (through resampling) if requested */
m_pcm_output.nSamplingRate = std::min(std::max((int)m_pcm_output.nSamplingRate, 8000), 192000);

m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), true);
m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), true);
m_omx_mixer.DisablePort(m_omx_mixer.GetInputPort(), false);

m_pcm_output.nPortIndex = m_omx_mixer.GetInputPort();
omx_err = m_omx_mixer.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_output);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - error m_omx_mixer(input) SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
}
m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false);
m_omx_mixer.EnablePort(m_omx_mixer.GetInputPort(), false);

return true;
}

Expand Down Expand Up @@ -657,6 +683,205 @@ bool COMXAudio::Initialize(OMXClock *clock, const OMXAudioConfig &config, uint64
return true;
}

bool COMXAudio::ChangeInputFormat(const CStdString& device, int iChannels, uint64_t channelMap,
COMXStreamInfo &hints, enum PCMLayout layout, unsigned int uiSampleRate, unsigned int uiBitsPerSample, bool boostOnDownmix,
OMXClock *clock, bool bUsePassthrough, bool bUseHWDecode, bool is_live, float fifo_size)
{
CSingleLock lock (m_critSection);
OMX_ERRORTYPE omx_err;

m_InputChannels = iChannels;

if(m_InputChannels == 0)
return false;

// if(hints.samplerate == 0)
// return false;

memset(m_input_channels, 0x0, sizeof(m_input_channels));
// memset(m_output_channels, 0x0, sizeof(m_output_channels));
memset(&m_wave_header, 0x0, sizeof(m_wave_header));

m_wave_header.Format.nChannels = 2;
m_wave_header.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;

// set the input format, and get the channel layout so we know what we need to open
if (!m_Passthrough && channelMap)
{
enum PCMChannels inLayout[OMX_AUDIO_MAXCHANNELS];
enum PCMChannels outLayout[OMX_AUDIO_MAXCHANNELS];
// force out layout to stereo if input is not multichannel - it gives the receiver a chance to upmix
if (channelMap == (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT) || channelMap == AV_CH_FRONT_CENTER)
layout = PCM_LAYOUT_2_0;
BuildChannelMap(inLayout, channelMap);
m_OutputChannels = BuildChannelMapCEA(outLayout, GetChannelLayout(layout));
CPCMRemap m_remap;
m_remap.Reset();
/*outLayout = */m_remap.SetInputFormat (m_InputChannels, inLayout, uiBitsPerSample / 8, uiSampleRate, layout, !m_normalize_downmix);
m_remap.SetOutputFormat(m_OutputChannels, outLayout);
m_remap.GetDownmixMatrix(m_downmix_matrix);
m_wave_header.dwChannelMask = channelMap;
BuildChannelMapOMX(m_input_channels, channelMap);
BuildChannelMapOMX(m_output_channels, GetChannelLayout(layout));
}

m_SampleRate = uiSampleRate;
m_BitsPerSample = uiBitsPerSample;

m_BytesPerSec = m_SampleRate * 2 << rounded_up_channels_shift[m_InputChannels];
m_BufferLen = m_BytesPerSec * AUDIO_BUFFER_SECONDS;
m_InputBytesPerSec = m_SampleRate * m_BitsPerSample * m_InputChannels >> 3;

// should be big enough that common formats (e.g. 6 channel DTS) fit in a single packet.
// we don't mind less common formats being split (e.g. ape/wma output large frames)
// 6 channel 32bpp float to 8 channel 16bpp in, so a full 48K input buffer will fit the output buffer
// m_ChunkLen = AUDIO_DECODE_OUTPUT_BUFFER * (m_InputChannels * m_BitsPerSample) >> (rounded_up_channels_shift[m_InputChannels] + 4);

m_wave_header.Samples.wSamplesPerBlock = 0;
m_wave_header.Format.nChannels = m_InputChannels;
m_wave_header.Format.nBlockAlign = m_InputChannels * (m_BitsPerSample >> 3);
// 0x8000 is custom format interpreted by GPU as WAVE_FORMAT_IEEE_FLOAT_PLANAR
m_wave_header.Format.wFormatTag = m_BitsPerSample == 32 ? 0x8000 : WAVE_FORMAT_PCM;
m_wave_header.Format.nSamplesPerSec = m_SampleRate;
m_wave_header.Format.nAvgBytesPerSec = m_BytesPerSec;
m_wave_header.Format.wBitsPerSample = m_BitsPerSample;
m_wave_header.Samples.wValidBitsPerSample = m_BitsPerSample;
m_wave_header.Format.cbSize = 0;
m_wave_header.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;

OMX_INIT_STRUCTURE(m_pcm_input);
memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
m_pcm_input.eNumData = OMX_NumericalDataSigned;
m_pcm_input.eEndian = OMX_EndianLittle;
m_pcm_input.bInterleaved = OMX_TRUE;
m_pcm_input.nBitPerSample = m_BitsPerSample;
m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear;
m_pcm_input.nChannels = m_InputChannels;
m_pcm_input.nSamplingRate = m_SampleRate;

// set up the number/size of buffers for decoder input
OMX_PARAM_PORTDEFINITIONTYPE port_param;
OMX_INIT_STRUCTURE(port_param);
port_param.nPortIndex = m_omx_decoder.GetInputPort();

omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_param);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "COMXAudio::ChangeInputFormat error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)\n", omx_err);
return false;
}

port_param.format.audio.eEncoding = m_eEncoding;
port_param.nBufferSize = m_ChunkLen;
port_param.nBufferCountActual = std::max(port_param.nBufferCountMin, 16U);

// set up the number/size of buffers for decoder output
OMX_INIT_STRUCTURE(port_param);
port_param.nPortIndex = m_omx_decoder.GetOutputPort();

omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_param);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "COMXAudio::ChangeInputFormat error get OMX_IndexParamPortDefinition (output) omx_err(0x%08x)\n", omx_err);
return false;
}

port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, m_BufferLen / port_param.nBufferSize);

if(m_eEncoding == OMX_AUDIO_CodingPCM)
{
/* setup decoder input */
OMX_INIT_STRUCTURE(m_pcm_output);
m_pcm_output.nPortIndex = m_omx_decoder.GetOutputPort();
omx_err = m_omx_decoder.GetParameter(OMX_IndexParamAudioPcm, &m_pcm_output);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - error m_omx_decoder GetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
}

printf( "m_omx_decoder OMX_IndexParamAudioPcm nChannels %d\n", m_pcm_output.nChannels );

//// decoder output -> mixer input
memcpy(m_pcm_output.eChannelMapping, m_input_channels, sizeof(m_input_channels));
// round up to power of 2
m_pcm_output.nChannels = m_InputChannels > 4 ? 8 : m_InputChannels > 2 ? 4 : m_InputChannels;
/* limit samplerate (through resampling) if requested */
m_pcm_output.nSamplingRate = std::min(std::max((int)m_pcm_output.nSamplingRate, 8000), 192000);

printf( "m_omx_decoder OMX_IndexParamAudioPcm nChannels %d\n", m_pcm_output.nChannels );

m_omx_decoder.DisablePort(m_omx_decoder.GetInputPort(), false);
m_omx_decoder.EnablePort(m_omx_decoder.GetInputPort(), true);

OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer();
if(omx_buffer == NULL)
{
CLog::Log(LOGERROR, "COMXAudio::ChangeInputFormat - buffer error 0x%08x", omx_err);
return false;
}

omx_buffer->nOffset = 0;
omx_buffer->nFilledLen = sizeof(m_wave_header);
if(omx_buffer->nFilledLen > omx_buffer->nAllocLen)
{
CLog::Log(LOGERROR, "COMXAudio::ChangeInputFormat - omx_buffer->nFilledLen > omx_buffer->nAllocLen");
return false;
}
memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen);
memcpy((unsigned char *)omx_buffer->pBuffer, &m_wave_header, omx_buffer->nFilledLen);
omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;

omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
if (omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err);
return false;
}
}
else if(m_HWDecode)
{
// send decoder config
if(m_extrasize > 0 && m_extradata != NULL)
{
OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer();

if(omx_buffer == NULL)
{
CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err);
return false;
}

omx_buffer->nOffset = 0;
omx_buffer->nFilledLen = m_extrasize;
if(omx_buffer->nFilledLen > omx_buffer->nAllocLen)
{
CLog::Log(LOGERROR, "%s::%s - omx_buffer->nFilledLen > omx_buffer->nAllocLen", CLASSNAME, __func__);
return false;
}

memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen);
memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen);
omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;

omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
if (omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err);
return false;
}
}
}

CLog::Log(LOGDEBUG, "COMXAudio::ChangeInputFormat Input bps %d samplerate %d channels %d buffer size %d bytes per second %d",
(int)m_pcm_input.nBitPerSample, (int)m_pcm_input.nSamplingRate, (int)m_pcm_input.nChannels, m_BufferLen, m_InputBytesPerSec);
PrintPCM(&m_pcm_input, std::string("input"));
CLog::Log(LOGDEBUG, "COMXAudio::ChangeInputFormat device %s passthrough %d hwdecode %d",
device.c_str(), m_Passthrough, m_HWDecode);

return true;
}


//***********************************************************************************************
bool COMXAudio::Deinitialize()
{
Expand Down
6 changes: 6 additions & 0 deletions OMXAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class COMXAudio
float GetMaxLevel(double &pts);
COMXAudio();
bool Initialize(OMXClock *clock, const OMXAudioConfig &config, uint64_t channelMap, unsigned int uiBitsPerSample);
bool ChangeInputFormat(const CStdString& device, int iChannels, uint64_t channelMap,
COMXStreamInfo &hints, enum PCMLayout layout, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool boostOnDownmix,
OMXClock *clock, bool bUsePassthrough = false, bool bUseHWDecode = false, bool is_live = false, float fifo_size = 0);

~COMXAudio();
bool PortSettingsChanged();

Expand All @@ -102,6 +106,7 @@ class COMXAudio
bool ApplyVolume();
void SubmitEOS();
bool IsEOS();
bool ToggleMonoTrack();

void Flush();

Expand Down Expand Up @@ -146,6 +151,7 @@ class COMXAudio
bool m_submitted_eos;
bool m_failed_eos;
OMXAudioConfig m_config;
int m_monotrack;

OMX_AUDIO_CHANNELTYPE m_input_channels[OMX_AUDIO_MAXCHANNELS];
OMX_AUDIO_CHANNELTYPE m_output_channels[OMX_AUDIO_MAXCHANNELS];
Expand Down
11 changes: 10 additions & 1 deletion OMXAudioCodecOMX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,16 @@ bool COMXAudioCodecOMX::Open(COMXStreamInfo &hints, enum PCMLayout layout)

m_dllAvCodec.avcodec_register_all();

pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
if( hints.codec != AV_CODEC_ID_AAC ){
pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
} else {
pCodec = m_dllAvCodec.avcodec_find_decoder_by_name("libfdk_aac");
if (!pCodec){
pCodec = m_dllAvCodec.avcodec_find_decoder(hints.codec);
}
}
printf( "[Audio codec is %s]\n", pCodec->long_name );

if (!pCodec)
{
CLog::Log(LOGDEBUG,"COMXAudioCodecOMX::Open() Unable to find codec %d", hints.codec);
Expand Down
30 changes: 26 additions & 4 deletions OMXPlayerAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,8 @@ bool OMXPlayerAudio::Decode(OMXPacket *pkt)
}

// for passthrough we only care about the codec and the samplerate
bool minor_change = channels != m_config.hints.channels ||
pkt->hints.bitspersample != m_config.hints.bitspersample ||
old_bitrate != new_bitrate;
bool minor_change = ( pkt->hints.bitspersample != m_hints.bitspersample ) ||
( old_bitrate != new_bitrate );

if(pkt->hints.codec != m_config.hints.codec ||
pkt->hints.samplerate != m_config.hints.samplerate ||
Expand Down Expand Up @@ -230,12 +229,30 @@ bool OMXPlayerAudio::Decode(OMXPacket *pkt)
while(data_len > 0)
{
int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len, dts, pts);
if( (len < 0) || (len > data_len) )
if( (len < 0) /*|| (len > data_len)*/ )
{
m_pAudioCodec->Reset();
break;
}

// check audio channel change
if( m_pAudioCodec->GetChannels() != 0 && m_pAudioCodec->GetChannels() < 7 &&
m_hints.channels != m_pAudioCodec->GetChannels() )
{

m_hints = pkt->hints;
m_hints.channels = m_pAudioCodec->GetChannels();
channels = m_pAudioCodec->GetChannels();

bool bAudioRenderOpen = false;
bAudioRenderOpen = m_decoder->ChangeInputFormat(m_device, m_hints.channels, m_pAudioCodec->GetChannelMap(),
m_hints, m_layout, m_hints.samplerate, m_pAudioCodec->GetBitsPerSample(), m_boost_on_downmix,
m_av_clock, m_passthrough, m_hw_decode, m_live, m_fifo_size);
if(!bAudioRenderOpen)
CLog::Log(LOGINFO, "m_decoder->ChangeInputFormat() failed");

}

data_dec+= len;
data_len -= len;

Expand Down Expand Up @@ -501,3 +518,8 @@ bool OMXPlayerAudio::IsEOS()
return m_packets.empty() && (!m_decoder || m_decoder->IsEOS());
}

bool OMXPlayerAudio::ToggleMonoTrack()
{
if(m_decoder)
m_decoder->ToggleMonoTrack();
}
1 change: 1 addition & 0 deletions OMXReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ bool OMXReader::Open(std::string filename, bool dump_format, bool live /* =false

if(m_filename.substr(0,6) == "mms://" || m_filename.substr(0,7) == "mmsh://" || m_filename.substr(0,7) == "mmst://" || m_filename.substr(0,7) == "mmsu://" ||
m_filename.substr(0,7) == "http://" || m_filename.substr(0,8) == "https://" ||
m_filename.substr(0,6) == "tcp://" ||
m_filename.substr(0,7) == "rtmp://" || m_filename.substr(0,6) == "udp://" ||
m_filename.substr(0,7) == "rtsp://" || m_filename.substr(0,6) == "rtp://" ||
m_filename.substr(0,6) == "ftp://" || m_filename.substr(0,7) == "sftp://" ||
Expand Down