545 lines
12 KiB
C++
545 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2005-2008 Team XBMC
|
|
* http://www.xbmc.org
|
|
*
|
|
* This Program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with XBMC; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
*/
|
|
|
|
#if (defined HAVE_CONFIG_H) && (!defined WIN32)
|
|
#include "config.h"
|
|
#elif defined(_WIN32)
|
|
#include "system.h"
|
|
#endif
|
|
|
|
#include "OMXPlayerAudio.h"
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include "linux/XMemUtils.h"
|
|
|
|
OMXPlayerAudio::OMXPlayerAudio()
|
|
{
|
|
m_open = false;
|
|
m_stream_id = -1;
|
|
m_pStream = NULL;
|
|
m_av_clock = NULL;
|
|
m_omx_reader = NULL;
|
|
m_decoder = NULL;
|
|
m_flush = false;
|
|
m_flush_requested = false;
|
|
m_cached_size = 0;
|
|
m_pAudioCodec = NULL;
|
|
m_player_error = true;
|
|
m_max_data_size = 3 * 1024 * 1024;
|
|
m_fifo_size = 2.0f;
|
|
m_live = false;
|
|
m_layout = PCM_LAYOUT_2_0;
|
|
m_CurrentVolume = 0.0f;
|
|
m_amplification = 0;
|
|
m_mute = false;
|
|
|
|
pthread_cond_init(&m_packet_cond, NULL);
|
|
pthread_cond_init(&m_audio_cond, NULL);
|
|
pthread_mutex_init(&m_lock, NULL);
|
|
pthread_mutex_init(&m_lock_decoder, NULL);
|
|
}
|
|
|
|
OMXPlayerAudio::~OMXPlayerAudio()
|
|
{
|
|
Close();
|
|
|
|
pthread_cond_destroy(&m_audio_cond);
|
|
pthread_cond_destroy(&m_packet_cond);
|
|
pthread_mutex_destroy(&m_lock);
|
|
pthread_mutex_destroy(&m_lock_decoder);
|
|
}
|
|
|
|
void OMXPlayerAudio::Lock()
|
|
{
|
|
if(m_use_thread)
|
|
pthread_mutex_lock(&m_lock);
|
|
}
|
|
|
|
void OMXPlayerAudio::UnLock()
|
|
{
|
|
if(m_use_thread)
|
|
pthread_mutex_unlock(&m_lock);
|
|
}
|
|
|
|
void OMXPlayerAudio::LockDecoder()
|
|
{
|
|
if(m_use_thread)
|
|
pthread_mutex_lock(&m_lock_decoder);
|
|
}
|
|
|
|
void OMXPlayerAudio::UnLockDecoder()
|
|
{
|
|
if(m_use_thread)
|
|
pthread_mutex_unlock(&m_lock_decoder);
|
|
}
|
|
|
|
bool OMXPlayerAudio::Open(COMXStreamInfo &hints, OMXClock *av_clock, OMXReader *omx_reader,
|
|
std::string device, bool passthrough, bool hw_decode,
|
|
bool boost_on_downmix, bool use_thread, bool is_live, enum PCMLayout layout, float queue_size, float fifo_size)
|
|
{
|
|
if(ThreadHandle())
|
|
Close();
|
|
|
|
if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllAvFormat.Load() || !av_clock)
|
|
return false;
|
|
|
|
m_dllAvFormat.av_register_all();
|
|
|
|
m_hints = hints;
|
|
m_av_clock = av_clock;
|
|
m_omx_reader = omx_reader;
|
|
m_device = device;
|
|
m_passthrough = false;
|
|
m_hw_decode = false;
|
|
m_use_passthrough = passthrough;
|
|
m_use_hw_decode = hw_decode;
|
|
m_boost_on_downmix = boost_on_downmix;
|
|
m_iCurrentPts = DVD_NOPTS_VALUE;
|
|
m_bAbort = false;
|
|
m_use_thread = use_thread;
|
|
m_flush = false;
|
|
m_flush_requested = false;
|
|
m_live = is_live;
|
|
m_layout = layout;
|
|
m_cached_size = 0;
|
|
m_pAudioCodec = NULL;
|
|
if (queue_size != 0.0)
|
|
m_max_data_size = queue_size * 1024 * 1024;
|
|
if (fifo_size != 0.0)
|
|
m_fifo_size = fifo_size;
|
|
|
|
m_player_error = OpenAudioCodec();
|
|
if(!m_player_error)
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
m_player_error = OpenDecoder();
|
|
if(!m_player_error)
|
|
{
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
if(m_use_thread)
|
|
Create();
|
|
|
|
m_open = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OMXPlayerAudio::Close()
|
|
{
|
|
m_bAbort = true;
|
|
|
|
Flush();
|
|
|
|
if(ThreadHandle())
|
|
{
|
|
Lock();
|
|
pthread_cond_broadcast(&m_packet_cond);
|
|
UnLock();
|
|
|
|
StopThread();
|
|
}
|
|
|
|
CloseDecoder();
|
|
CloseAudioCodec();
|
|
|
|
m_open = false;
|
|
m_stream_id = -1;
|
|
m_iCurrentPts = DVD_NOPTS_VALUE;
|
|
m_pStream = NULL;
|
|
|
|
m_dllAvUtil.Unload();
|
|
m_dllAvCodec.Unload();
|
|
m_dllAvFormat.Unload();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool OMXPlayerAudio::Decode(OMXPacket *pkt)
|
|
{
|
|
if(!pkt)
|
|
return false;
|
|
|
|
/* last decoder reinit went wrong */
|
|
if(!m_decoder || !m_pAudioCodec)
|
|
return true;
|
|
|
|
if(!m_omx_reader->IsActive(OMXSTREAM_AUDIO, pkt->stream_index))
|
|
return true;
|
|
|
|
int channels = pkt->hints.channels;
|
|
|
|
unsigned int old_bitrate = m_hints.bitrate;
|
|
unsigned int new_bitrate = pkt->hints.bitrate;
|
|
|
|
/* only check bitrate changes on CODEC_ID_DTS, CODEC_ID_AC3, CODEC_ID_EAC3 */
|
|
if(m_hints.codec != CODEC_ID_DTS && m_hints.codec != CODEC_ID_AC3 && m_hints.codec != CODEC_ID_EAC3)
|
|
{
|
|
new_bitrate = old_bitrate = 0;
|
|
}
|
|
|
|
// for passthrough we only care about the codec and the samplerate
|
|
bool minor_change = channels != m_hints.channels ||
|
|
pkt->hints.bitspersample != m_hints.bitspersample ||
|
|
old_bitrate != new_bitrate;
|
|
|
|
if(pkt->hints.codec != m_hints.codec ||
|
|
pkt->hints.samplerate != m_hints.samplerate ||
|
|
(!m_passthrough && minor_change))
|
|
{
|
|
printf("C : %d %d %d %d %d\n", m_hints.codec, m_hints.channels, m_hints.samplerate, m_hints.bitrate, m_hints.bitspersample);
|
|
printf("N : %d %d %d %d %d\n", pkt->hints.codec, channels, pkt->hints.samplerate, pkt->hints.bitrate, pkt->hints.bitspersample);
|
|
|
|
|
|
CloseDecoder();
|
|
CloseAudioCodec();
|
|
|
|
m_hints = pkt->hints;
|
|
|
|
m_player_error = OpenAudioCodec();
|
|
if(!m_player_error)
|
|
return false;
|
|
|
|
m_player_error = OpenDecoder();
|
|
if(!m_player_error)
|
|
return false;
|
|
}
|
|
|
|
CLog::Log(LOGINFO, "CDVDPlayerAudio::Decode dts:%.0f pts:%.0f size:%d", pkt->dts, pkt->pts, pkt->size);
|
|
|
|
if(pkt->pts != DVD_NOPTS_VALUE)
|
|
m_iCurrentPts = pkt->pts;
|
|
else if(pkt->dts != DVD_NOPTS_VALUE)
|
|
m_iCurrentPts = pkt->dts;
|
|
|
|
const uint8_t *data_dec = pkt->data;
|
|
int data_len = pkt->size;
|
|
|
|
if(!m_passthrough && !m_hw_decode)
|
|
{
|
|
double dts = pkt->dts, pts=pkt->pts;
|
|
while(data_len > 0)
|
|
{
|
|
int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len, dts, pts);
|
|
if( (len < 0) || (len > data_len) )
|
|
{
|
|
m_pAudioCodec->Reset();
|
|
break;
|
|
}
|
|
|
|
data_dec+= len;
|
|
data_len -= len;
|
|
|
|
uint8_t *decoded;
|
|
int decoded_size = m_pAudioCodec->GetData(&decoded, dts, pts);
|
|
|
|
if(decoded_size <=0)
|
|
continue;
|
|
|
|
while((int) m_decoder->GetSpace() < decoded_size)
|
|
{
|
|
OMXClock::OMXSleep(10);
|
|
if(m_flush_requested) return true;
|
|
}
|
|
|
|
int ret = 0;
|
|
|
|
ret = m_decoder->AddPackets(decoded, decoded_size, dts, pts, m_pAudioCodec->GetFrameSize());
|
|
if(ret != decoded_size)
|
|
{
|
|
printf("error ret %d decoded_size %d\n", ret, decoded_size);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while((int) m_decoder->GetSpace() < pkt->size)
|
|
{
|
|
OMXClock::OMXSleep(10);
|
|
if(m_flush_requested) return true;
|
|
}
|
|
|
|
m_decoder->AddPackets(pkt->data, pkt->size, pkt->dts, pkt->pts, 0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OMXPlayerAudio::Process()
|
|
{
|
|
OMXPacket *omx_pkt = NULL;
|
|
|
|
while(!m_bStop && !m_bAbort)
|
|
{
|
|
Lock();
|
|
if(m_packets.empty())
|
|
pthread_cond_wait(&m_packet_cond, &m_lock);
|
|
UnLock();
|
|
|
|
if(m_bAbort)
|
|
break;
|
|
|
|
Lock();
|
|
if(m_flush && omx_pkt)
|
|
{
|
|
OMXReader::FreePacket(omx_pkt);
|
|
omx_pkt = NULL;
|
|
m_flush = false;
|
|
}
|
|
else if(!omx_pkt && !m_packets.empty())
|
|
{
|
|
omx_pkt = m_packets.front();
|
|
m_cached_size -= omx_pkt->size;
|
|
m_packets.pop_front();
|
|
}
|
|
UnLock();
|
|
|
|
LockDecoder();
|
|
if(m_flush && omx_pkt)
|
|
{
|
|
OMXReader::FreePacket(omx_pkt);
|
|
omx_pkt = NULL;
|
|
m_flush = false;
|
|
}
|
|
else if(omx_pkt && Decode(omx_pkt))
|
|
{
|
|
OMXReader::FreePacket(omx_pkt);
|
|
omx_pkt = NULL;
|
|
}
|
|
UnLockDecoder();
|
|
}
|
|
|
|
if(omx_pkt)
|
|
OMXReader::FreePacket(omx_pkt);
|
|
}
|
|
|
|
void OMXPlayerAudio::Flush()
|
|
{
|
|
m_flush_requested = true;
|
|
Lock();
|
|
LockDecoder();
|
|
if(m_pAudioCodec)
|
|
m_pAudioCodec->Reset();
|
|
m_flush_requested = false;
|
|
m_flush = true;
|
|
while (!m_packets.empty())
|
|
{
|
|
OMXPacket *pkt = m_packets.front();
|
|
m_packets.pop_front();
|
|
OMXReader::FreePacket(pkt);
|
|
}
|
|
m_iCurrentPts = DVD_NOPTS_VALUE;
|
|
m_cached_size = 0;
|
|
if(m_decoder)
|
|
m_decoder->Flush();
|
|
UnLockDecoder();
|
|
UnLock();
|
|
}
|
|
|
|
bool OMXPlayerAudio::AddPacket(OMXPacket *pkt)
|
|
{
|
|
bool ret = false;
|
|
|
|
if(!pkt)
|
|
return ret;
|
|
|
|
if(m_bStop || m_bAbort)
|
|
return ret;
|
|
|
|
if((m_cached_size + pkt->size) < m_max_data_size)
|
|
{
|
|
Lock();
|
|
m_cached_size += pkt->size;
|
|
m_packets.push_back(pkt);
|
|
UnLock();
|
|
ret = true;
|
|
pthread_cond_broadcast(&m_packet_cond);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool OMXPlayerAudio::OpenAudioCodec()
|
|
{
|
|
m_pAudioCodec = new COMXAudioCodecOMX();
|
|
|
|
if(!m_pAudioCodec->Open(m_hints, m_layout))
|
|
{
|
|
delete m_pAudioCodec; m_pAudioCodec = NULL;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OMXPlayerAudio::CloseAudioCodec()
|
|
{
|
|
if(m_pAudioCodec)
|
|
delete m_pAudioCodec;
|
|
m_pAudioCodec = NULL;
|
|
}
|
|
|
|
bool OMXPlayerAudio::IsPassthrough(COMXStreamInfo hints)
|
|
{
|
|
if(m_device == "omx:local")
|
|
return false;
|
|
|
|
bool passthrough = false;
|
|
|
|
if(hints.codec == CODEC_ID_AC3)
|
|
{
|
|
passthrough = true;
|
|
}
|
|
if(hints.codec == CODEC_ID_EAC3)
|
|
{
|
|
passthrough = true;
|
|
}
|
|
if(hints.codec == CODEC_ID_DTS)
|
|
{
|
|
passthrough = true;
|
|
}
|
|
|
|
return passthrough;
|
|
}
|
|
|
|
bool OMXPlayerAudio::OpenDecoder()
|
|
{
|
|
bool bAudioRenderOpen = false;
|
|
|
|
m_decoder = new COMXAudio();
|
|
|
|
if(m_use_passthrough)
|
|
m_passthrough = IsPassthrough(m_hints);
|
|
|
|
if(!m_passthrough && m_use_hw_decode)
|
|
m_hw_decode = COMXAudio::HWDecode(m_hints.codec);
|
|
|
|
if(m_passthrough)
|
|
m_hw_decode = false;
|
|
|
|
bAudioRenderOpen = m_decoder->Initialize(m_device, 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);
|
|
|
|
m_codec_name = m_omx_reader->GetCodecName(OMXSTREAM_AUDIO);
|
|
|
|
if(!bAudioRenderOpen)
|
|
{
|
|
delete m_decoder;
|
|
m_decoder = NULL;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if(m_passthrough)
|
|
{
|
|
printf("Audio codec %s passthrough channels %d samplerate %d bitspersample %d\n",
|
|
m_codec_name.c_str(), m_hints.channels, m_hints.samplerate, m_hints.bitspersample);
|
|
}
|
|
else
|
|
{
|
|
printf("Audio codec %s channels %d samplerate %d bitspersample %d\n",
|
|
m_codec_name.c_str(), m_hints.channels, m_hints.samplerate, m_hints.bitspersample);
|
|
}
|
|
}
|
|
// setup current volume settings
|
|
m_decoder->SetVolume(m_CurrentVolume);
|
|
m_decoder->SetMute(m_mute);
|
|
m_decoder->SetDynamicRangeCompression(m_amplification);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OMXPlayerAudio::CloseDecoder()
|
|
{
|
|
if(m_decoder)
|
|
delete m_decoder;
|
|
m_decoder = NULL;
|
|
return true;
|
|
}
|
|
|
|
double OMXPlayerAudio::GetDelay()
|
|
{
|
|
if(m_decoder)
|
|
return m_decoder->GetDelay();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
double OMXPlayerAudio::GetCacheTime()
|
|
{
|
|
if(m_decoder)
|
|
return m_decoder->GetCacheTime();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
double OMXPlayerAudio::GetCacheTotal()
|
|
{
|
|
if(m_decoder)
|
|
return m_decoder->GetCacheTotal();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void OMXPlayerAudio::SubmitEOS()
|
|
{
|
|
if(m_decoder)
|
|
m_decoder->SubmitEOS();
|
|
}
|
|
|
|
bool OMXPlayerAudio::IsEOS()
|
|
{
|
|
return m_packets.empty() && (!m_decoder || m_decoder->IsEOS());
|
|
}
|
|
|
|
void OMXPlayerAudio::WaitCompletion()
|
|
{
|
|
if(!m_decoder)
|
|
return;
|
|
|
|
unsigned int nTimeOut = m_fifo_size * 1000;
|
|
while(nTimeOut)
|
|
{
|
|
if(IsEOS())
|
|
{
|
|
CLog::Log(LOGDEBUG, "%s::%s - got eos\n", "OMXPlayerAudio", __func__);
|
|
break;
|
|
}
|
|
|
|
if(nTimeOut == 0)
|
|
{
|
|
CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", "OMXPlayerAudio", __func__);
|
|
break;
|
|
}
|
|
OMXClock::OMXSleep(50);
|
|
nTimeOut -= 50;
|
|
}
|
|
}
|
|
|