return m_current.pts + min(m_current.duration, (timestamp - m_current.timestamp));
}
-/** \brief Reallocs the memory block pointed to by src by the size len.
-* \param[in] src Pointer to a memory block.
-* \param[in] len New size of the memory block.
-* \exception realloc failed
-* \return A pointer to the reallocated memory block.
-*/
-static void* realloc_or_free(void* src, int len) throw(exception)
-{
- void* new_pBuffer = realloc(src, len);
- if (new_pBuffer)
- return new_pBuffer;
- else
- {
- CLog::Log(LOGERROR, "DVDAUDIO - %s : could not realloc the buffer", __FUNCTION__);
- free(src);
- throw exception();
- }
-}
-
CDVDAudio::CDVDAudio(volatile bool &bStop)
: m_bStop(bStop)
{
m_pAudioStream = NULL;
m_pAudioCallback = NULL;
- m_iBufferSize = 0;
- m_dwPacketSize = 0;
- m_pBuffer = NULL;
m_bPassthrough = false;
m_iBitsPerSample = 0;
m_iBitrate = 0;
CSingleLock lock (m_critSection);
if (m_pAudioStream)
CAEFactory::FreeStream(m_pAudioStream);
-
- free(m_pBuffer);
}
bool CDVDAudio::Create(const DVDAudioFrame &audioframe, AVCodecID codec, bool needresampler)
m_iBitsPerSample = audioframe.bits_per_sample;
m_bPassthrough = audioframe.passthrough;
m_channelLayout = audioframe.channel_layout;
- m_dwPacketSize = m_pAudioStream->GetFrameSize();
if(m_channelLayout.Count() && m_iBitrate && m_iBitsPerSample)
m_SecondsPerByte = 1.0 / (m_channelLayout.Count() * m_iBitrate * (m_iBitsPerSample>>3));
else
m_SecondsPerByte = 0.0;
- m_iBufferSize = 0;
SetDynamicRangeCompression((long)(CMediaSettings::Get().GetCurrentVideoSettings().m_VolumeAmplification * 100));
if (m_pAudioCallback)
if (m_pAudioStream)
CAEFactory::FreeStream(m_pAudioStream);
- free(m_pBuffer);
- m_pBuffer = NULL;
- m_dwPacketSize = 0;
m_pAudioStream = NULL;
- m_iBufferSize = 0;
m_iBitrate = 0;
m_iBitsPerSample = 0;
m_bPassthrough = false;
m_time.Flush();
}
-unsigned int CDVDAudio::AddPacketsRenderer(unsigned char* data, unsigned int len, CSingleLock &lock)
+unsigned int CDVDAudio::AddPackets(const DVDAudioFrame &audioframe)
{
+ CSingleLock lock (m_critSection);
+
if(!m_pAudioStream)
return 0;
//Calculate a timeout when this definitely should be done
double timeout;
- timeout = DVD_SEC_TO_TIME(m_pAudioStream->GetDelay() + len * m_SecondsPerByte);
+ timeout = DVD_SEC_TO_TIME(m_pAudioStream->GetDelay() + audioframe.nb_frames*audioframe.framesize * m_SecondsPerByte);
timeout += DVD_SEC_TO_TIME(1.0);
timeout += CDVDClock::GetAbsoluteClock();
- unsigned int total = len;
+ unsigned int total = audioframe.nb_frames;
+ unsigned int frames = audioframe.nb_frames;
+ unsigned int offset = 0;
do
{
- unsigned int copied = m_pAudioStream->AddData(data, len);
- data += copied;
- len -= copied;
- if (len < m_dwPacketSize)
+ unsigned int copied = m_pAudioStream->AddData(audioframe.data, offset, frames);
+ offset += copied;
+ frames -= copied;
+ if (frames <= 0)
break;
if (copied == 0 && timeout < CDVDClock::GetAbsoluteClock())
lock.Enter();
} while (!m_bStop);
- return total - len;
-}
-
-unsigned int CDVDAudio::AddPackets(const DVDAudioFrame &audioframe)
-{
- CSingleLock lock (m_critSection);
-
- unsigned char* data = audioframe.data;
- unsigned int len = audioframe.size;
-
- unsigned int total = len;
- unsigned int copied;
-
- if (m_iBufferSize > 0) // See if there are carryover bytes from the last call. need to add them 1st.
- {
- copied = std::min(m_dwPacketSize - m_iBufferSize % m_dwPacketSize, len); // Smaller of either the data provided or the leftover data
- if(copied)
- {
- m_pBuffer = (uint8_t*)realloc_or_free(m_pBuffer, m_iBufferSize + copied);
- memcpy(m_pBuffer + m_iBufferSize, data, copied); // Tack the caller's data onto the end of the buffer
- data += copied; // Move forward in caller's data
- len -= copied; // Decrease amount of data available from caller
- m_iBufferSize += copied; // Increase amount of data available in buffer
- }
-
- if(m_iBufferSize < m_dwPacketSize) // If we don't have enough data to give to the renderer, wait until next time
- return copied;
-
- if(AddPacketsRenderer(m_pBuffer, m_iBufferSize, lock) != m_iBufferSize)
- {
- m_iBufferSize = 0;
- CLog::Log(LOGERROR, "%s - failed to add leftover bytes to render", __FUNCTION__);
- return copied;
- }
-
- m_iBufferSize = 0;
- if (!len)
- return copied; // We used up all the caller's data
- }
-
- copied = AddPacketsRenderer(data, len, lock);
- data += copied;
- len -= copied;
-
- // if we have more data left, save it for the next call to this funtion
- if (len > 0 && !m_bStop)
- {
- m_pBuffer = (uint8_t*)realloc_or_free(m_pBuffer, len);
- m_iBufferSize = len;
- memcpy(m_pBuffer, data, len);
- }
-
- double time_added = DVD_SEC_TO_TIME(m_SecondsPerByte * (data - audioframe.data));
- double delay = GetDelay();
- double timestamp = CDVDClock::GetAbsoluteClock();
+ double time_added = DVD_SEC_TO_TIME(m_SecondsPerByte * audioframe.nb_frames * audioframe.framesize);
+ double delay = GetDelay();
+ double timestamp = CDVDClock::GetAbsoluteClock();
m_time.Add(audioframe.pts, delay - time_added, audioframe.duration, timestamp);
- return total;
+ return total - frames;
}
void CDVDAudio::Finish()
CSingleLock lock (m_critSection);
if (!m_pAudioStream)
return;
-
- unsigned int silence = m_dwPacketSize - m_iBufferSize % m_dwPacketSize;
-
- if(silence > 0 && m_iBufferSize > 0)
- {
- CLog::Log(LOGDEBUG, "CDVDAudio::Drain - adding %d bytes of silence, buffer size: %d, chunk size: %d", silence, m_iBufferSize, m_dwPacketSize);
- m_pBuffer = (uint8_t*)realloc_or_free(m_pBuffer, m_iBufferSize + silence);
- memset(m_pBuffer+m_iBufferSize, 0, silence);
- m_iBufferSize += silence;
- }
-
- if(AddPacketsRenderer(m_pBuffer, m_iBufferSize, lock) != m_iBufferSize)
- CLog::Log(LOGERROR, "CDVDAudio::Drain - failed to play the final %d bytes", m_iBufferSize);
-
- m_iBufferSize = 0;
}
void CDVDAudio::Drain()
if(m_pAudioStream)
delay = m_pAudioStream->GetDelay();
- delay += m_SecondsPerByte * m_iBufferSize;
-
return delay * DVD_TIME_BASE;
}
{
m_pAudioStream->Flush();
}
- m_iBufferSize = 0;
m_time.Flush();
}
if(m_pAudioStream)
delay = m_pAudioStream->GetCacheTime();
- delay += m_SecondsPerByte * m_iBufferSize;
-
return delay;
}
IAEStream *m_pAudioStream;
protected:
CPTSOutputQueue m_time;
- unsigned int AddPacketsRenderer(unsigned char* data, unsigned int len, CSingleLock &lock);
- uint8_t* m_pBuffer; // should be [m_dwPacketSize]
- unsigned int m_iBufferSize;
- unsigned int m_dwPacketSize;
CCriticalSection m_critSection;
int m_iBitrate;
typedef struct stDVDAudioFrame
{
- uint8_t* data;
+ uint8_t* data[16];
double pts;
double duration;
- unsigned int size;
+ unsigned int nb_frames;
+ unsigned int framesize;
+ unsigned int planes;
int channel_count;
int encoded_channel_count;
*/
virtual void GetData(DVDAudioFrame &frame)
{
- frame.size = GetData(&frame.data);
- if(frame.size == 0u)
+ frame.nb_frames = 0;
+ frame.data_format = GetDataFormat();
+ frame.channel_count = GetChannels();
+ frame.framesize = (CAEUtil::DataFormatToBits(frame.data_format) >> 3) * frame.channel_count;
+ if(frame.framesize == 0)
return;
+ frame.nb_frames = GetData(frame.data)/frame.framesize;
frame.channel_layout = GetChannelMap();
frame.channel_count = GetChannels();
+ frame.planes = AE_IS_PLANAR(frame.data_format) ? frame.channel_count : 1;
frame.encoded_channel_count = GetEncodedChannels();
- frame.data_format = GetDataFormat();
frame.bits_per_sample = CAEUtil::DataFormatToBits(frame.data_format);
frame.sample_rate = GetSampleRate();
frame.encoded_sample_rate = GetEncodedSampleRate();
frame.passthrough = NeedPassthrough();
frame.pts = DVD_NOPTS_VALUE;
// compute duration.
- int n = (frame.channel_count * frame.bits_per_sample * frame.sample_rate)>>3;
- if (n)
- frame.duration = ((double)frame.size * DVD_TIME_BASE) / n;
+ if (frame.sample_rate)
+ frame.duration = ((double)frame.nb_frames * DVD_TIME_BASE) / frame.sample_rate;
else
frame.duration = 0.0;
}
CDVDAudioCodecFFmpeg::CDVDAudioCodecFFmpeg() : CDVDAudioCodec()
{
- m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
- m_iBufferTotalSize2 = 0;
- m_pBuffer2 = NULL;
-
m_iBuffered = 0;
m_pCodecContext = NULL;
- m_pConvert = NULL;
m_bOpenedCodec = false;
m_channels = 0;
m_pFrame1 = NULL;
m_iSampleFormat = AV_SAMPLE_FMT_NONE;
+ m_gotFrame = 0;
}
CDVDAudioCodecFFmpeg::~CDVDAudioCodecFFmpeg()
if (m_pFrame1) av_free(m_pFrame1);
m_pFrame1 = NULL;
- if (m_pConvert)
- swr_free(&m_pConvert);
-
- if (m_pBuffer2)
- av_freep(&m_pBuffer2);
-
if (m_pCodecContext)
{
if (m_bOpenedCodec) avcodec_close(m_pCodecContext);
m_pCodecContext = NULL;
}
- m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
- m_iBufferTotalSize2 = 0;
m_iBuffered = 0;
}
int CDVDAudioCodecFFmpeg::Decode(uint8_t* pData, int iSize)
{
- int iBytesUsed, got_frame;
+ int iBytesUsed;
if (!m_pCodecContext) return -1;
- m_iBufferSize2 = 0;
-
AVPacket avpkt;
av_init_packet(&avpkt);
avpkt.data = pData;
avpkt.size = iSize;
iBytesUsed = avcodec_decode_audio4( m_pCodecContext
, m_pFrame1
- , &got_frame
+ , &m_gotFrame
, &avpkt);
- if (iBytesUsed < 0 || !got_frame)
+ if (iBytesUsed < 0 || !m_gotFrame)
{
- m_iBufferSize1 = 0;
return iBytesUsed;
}
- m_iBufferSize1 = m_pFrame1->nb_samples * m_pCodecContext->channels * av_get_bytes_per_sample(m_pCodecContext->sample_fmt);
/* some codecs will attempt to consume more data than what we gave */
if (iBytesUsed > iSize)
iBytesUsed = iSize;
}
- if(m_iBufferSize1 == 0 && iBytesUsed >= 0)
+ if(iBytesUsed >= 0)
m_iBuffered += iBytesUsed;
else
m_iBuffered = 0;
- bool convert = false;
- switch(m_pCodecContext->sample_fmt)
- {
- case AV_SAMPLE_FMT_U8:
- case AV_SAMPLE_FMT_S16:
- case AV_SAMPLE_FMT_S32:
- case AV_SAMPLE_FMT_FLT:
- case AV_SAMPLE_FMT_DBL:
- break;
- case AV_SAMPLE_FMT_NONE:
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - invalid data format");
- return -1;
- default:
- convert = true;
- }
- if(convert)
- ConvertToFloat();
-
return iBytesUsed;
}
-void CDVDAudioCodecFFmpeg::ConvertToFloat()
-{
- if(m_pCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT && m_iBufferSize1 > 0)
- {
- if(m_pConvert && (m_pCodecContext->sample_fmt != m_iSampleFormat || m_channels != m_pCodecContext->channels))
- swr_free(&m_pConvert);
-
- if(!m_pConvert)
- {
- m_iSampleFormat = m_pCodecContext->sample_fmt;
- m_pConvert = swr_alloc_set_opts(NULL,
- av_get_default_channel_layout(m_pCodecContext->channels), AV_SAMPLE_FMT_FLT, m_pCodecContext->sample_rate,
- av_get_default_channel_layout(m_pCodecContext->channels), m_pCodecContext->sample_fmt, m_pCodecContext->sample_rate,
- 0, NULL);
-
- if(!m_pConvert || swr_init(m_pConvert) < 0)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to convert %d to AV_SAMPLE_FMT_FLT", m_pCodecContext->sample_fmt);
- m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
- return;
- }
- }
-
- int needed_buf_size = av_samples_get_buffer_size(NULL, m_pCodecContext->channels, m_pFrame1->nb_samples, AV_SAMPLE_FMT_FLT, 0);
- if(m_iBufferTotalSize2 < needed_buf_size)
- {
- m_pBuffer2 = (uint8_t*)av_realloc(m_pBuffer2, needed_buf_size);
- if(!m_pBuffer2)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to allocate a %i bytes buffer for resampling", needed_buf_size);
- m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
- m_iBufferTotalSize2 = 0;
- return;
- }
- m_iBufferTotalSize2 = needed_buf_size;
- }
-
- int outsamples;
- outsamples = swr_convert(m_pConvert, &m_pBuffer2, m_iBufferTotalSize2, (const uint8_t**)m_pFrame1->extended_data, m_pFrame1->nb_samples);
-
- if(outsamples < 0)
- {
- CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Decode - Unable to convert %d to AV_SAMPLE_FMT_FLT", (int)m_pCodecContext->sample_fmt);
- m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
- return;
- }
-
- if(outsamples < m_pFrame1->nb_samples)
- {
- CLog::Log(LOGWARNING, "CDVDAudioCodecFFmpeg::Decode - Resampler produced less samples than what it was given");
- }
-
- m_iBufferSize1 = 0;
- m_iBufferSize2 = m_pFrame1->nb_samples * m_pCodecContext->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT);
- }
-}
-
int CDVDAudioCodecFFmpeg::GetData(uint8_t** dst)
{
- if(m_iBufferSize1)
- {
- *dst = m_pFrame1->data[0];
- return m_iBufferSize1;
- }
-
- if(m_iBufferSize2)
+ if(m_gotFrame)
{
- *dst = m_pBuffer2;
- return m_iBufferSize2;
+ int planes = av_sample_fmt_is_planar(m_pCodecContext->sample_fmt) ? m_pFrame1->channels : 1;
+ for (int i=0; i<planes; i++)
+ dst[i] = m_pFrame1->extended_data[i];
+ m_gotFrame = 0;
+ return m_pFrame1->nb_samples * m_pFrame1->channels * av_get_bytes_per_sample(m_pCodecContext->sample_fmt);
}
return 0;
void CDVDAudioCodecFFmpeg::Reset()
{
if (m_pCodecContext) avcodec_flush_buffers(m_pCodecContext);
- m_iBufferSize1 = 0;
- m_iBufferSize2 = 0;
m_iBuffered = 0;
+ m_gotFrame = 0;
}
int CDVDAudioCodecFFmpeg::GetChannels()
switch(m_pCodecContext->sample_fmt)
{
case AV_SAMPLE_FMT_U8 : return AE_FMT_U8;
+ case AV_SAMPLE_FMT_U8P : return AE_FMT_U8P;
case AV_SAMPLE_FMT_S16: return AE_FMT_S16NE;
+ case AV_SAMPLE_FMT_S16P: return AE_FMT_S16NEP;
case AV_SAMPLE_FMT_S32: return AE_FMT_S32NE;
+ case AV_SAMPLE_FMT_S32P: return AE_FMT_S32NEP;
case AV_SAMPLE_FMT_FLT: return AE_FMT_FLOAT;
+ case AV_SAMPLE_FMT_FLTP: return AE_FMT_FLOATP;
case AV_SAMPLE_FMT_DBL: return AE_FMT_DOUBLE;
+ case AV_SAMPLE_FMT_DBLP: return AE_FMT_DOUBLEP;
case AV_SAMPLE_FMT_NONE:
+ default:
CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::GetDataFormat - invalid data format");
return AE_FMT_INVALID;
- default:
- return AE_FMT_FLOAT;
}
}
protected:
AVCodecContext* m_pCodecContext;
- SwrContext* m_pConvert;
enum AVSampleFormat m_iSampleFormat;
CAEChannelInfo m_channelLayout;
AVFrame* m_pFrame1;
- int m_iBufferSize1;
- uint8_t* m_pBuffer2;
- int m_iBufferSize2;
- int m_iBufferTotalSize2;
+ int m_gotFrame;
bool m_bOpenedCodec;
int m_iBuffered;
int result = 0;
// make sure the sent frame is clean
- memset(&audioframe, 0, sizeof(DVDAudioFrame));
+ audioframe.nb_frames = 0;
while (!m_bStop)
{
// get decoded data and the size of it
m_pAudioCodec->GetData(audioframe);
- if (audioframe.size == 0)
+ if (audioframe.nb_frames == 0)
continue;
if (audioframe.pts == DVD_NOPTS_VALUE)
break;
}
- if( audioframe.size == 0 )
+ if( audioframe.nb_frames == 0 )
continue;
packetadded = true;
// Zero out the frame data if we are supposed to silence the audio
if (m_silence)
- memset(audioframe.data, 0, audioframe.size);
+ {
+ int size = audioframe.nb_frames * audioframe.framesize * audioframe.channel_count / audioframe.planes;
+ for (int i=0; i<audioframe.planes; i++)
+ memset(audioframe.data[i], 0, size);
+ }
if(result & DECODE_FLAG_DROP)
{
// keep output times in sync
- m_dvdAudio.SetPlayingPts(m_audioClock);
+ m_dvdAudio.SetPlayingPts(m_audioClock);
}
else
{