2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
23 #elif defined(TARGET_WINDOWS)
27 #include "OMXPlayerAudio.h"
33 #include "linux/XMemUtils.h"
34 #include "utils/BitstreamStats.h"
36 #include "DVDDemuxers/DVDDemuxUtils.h"
37 #include "cores/AudioEngine/Utils/AEUtil.h"
38 #include "utils/MathUtils.h"
39 #include "settings/AdvancedSettings.h"
40 #include "settings/Settings.h"
41 #include "utils/TimeUtils.h"
43 #include "OMXPlayer.h"
44 #include "linux/RBP.h"
49 class COMXMsgAudioCodecChange : public CDVDMsg
52 COMXMsgAudioCodecChange(const CDVDStreamInfo &hints, COMXAudioCodecOMX* codec)
53 : CDVDMsg(GENERAL_STREAMCHANGE)
57 ~COMXMsgAudioCodecChange()
61 COMXAudioCodecOMX *m_codec;
62 CDVDStreamInfo m_hints;
65 OMXPlayerAudio::OMXPlayerAudio(OMXClock *av_clock, CDVDMessageQueue& parent)
66 : CThread("OMXPlayerAudio")
67 , m_messageQueue("audio")
68 , m_messageParent(parent)
70 m_av_clock = av_clock;
72 m_speed = DVD_PLAYSPEED_NORMAL;
75 m_audioClock = DVD_NOPTS_VALUE;
76 m_buffer_empty = false;
78 m_DecoderOpen = false;
80 m_hints_current.Clear();
82 bool small_mem = g_RBP.GetArmMem() < 256;
83 m_messageQueue.SetMaxDataSize((small_mem ? 3:6) * 1024 * 1024);
85 m_messageQueue.SetMaxTimeSize(8.0);
86 m_use_passthrough = false;
87 m_passthrough = false;
88 m_use_hw_decode = false;
95 OMXPlayerAudio::~OMXPlayerAudio()
99 m_DllBcmHost.Unload();
102 bool OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints)
104 if(!m_DllBcmHost.Load())
109 COMXAudioCodecOMX *codec = new COMXAudioCodecOMX();
111 if(!codec || !codec->Open(hints))
113 CLog::Log(LOGERROR, "Unsupported audio codec");
114 delete codec; codec = NULL;
118 if(m_messageQueue.IsInited())
119 m_messageQueue.Put(new COMXMsgAudioCodecChange(hints, codec), 0);
122 OpenStream(hints, codec);
123 m_messageQueue.Init();
124 CLog::Log(LOGNOTICE, "Creating audio thread");
131 void OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec)
133 SAFE_DELETE(m_pAudioCodec);
136 m_pAudioCodec = codec;
138 if(m_hints.bitspersample == 0)
139 m_hints.bitspersample = 16;
141 m_speed = DVD_PLAYSPEED_NORMAL;
142 m_audioClock = DVD_NOPTS_VALUE;
148 m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
149 m_use_passthrough = (CSettings::Get().GetInt("audiooutput.mode") == AUDIO_HDMI) ? true : false ;
150 m_use_hw_decode = g_advancedSettings.m_omxHWAudioDecode;
153 bool OMXPlayerAudio::CloseStream(bool bWaitForBuffers)
155 // wait until buffers are empty
156 if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
158 m_messageQueue.Abort();
163 m_messageQueue.End();
167 m_pAudioCodec->Dispose();
168 delete m_pAudioCodec;
169 m_pAudioCodec = NULL;
174 m_speed = DVD_PLAYSPEED_NORMAL;
180 void OMXPlayerAudio::OnStartup()
184 void OMXPlayerAudio::OnExit()
186 CLog::Log(LOGNOTICE, "thread end: OMXPlayerAudio::OnExit()");
189 bool OMXPlayerAudio::CodecChange()
191 unsigned int old_bitrate = m_hints.bitrate;
192 unsigned int new_bitrate = m_hints_current.bitrate;
196 m_hints.channels = m_pAudioCodec->GetChannels();
197 m_hints.samplerate = m_pAudioCodec->GetSampleRate();
200 /* only check bitrate changes on AV_CODEC_ID_DTS, AV_CODEC_ID_AC3, AV_CODEC_ID_EAC3 */
201 if(m_hints.codec != AV_CODEC_ID_DTS && m_hints.codec != AV_CODEC_ID_AC3 && m_hints.codec != AV_CODEC_ID_EAC3)
202 new_bitrate = old_bitrate = 0;
204 if(m_hints_current.codec != m_hints.codec ||
205 m_hints_current.channels != m_hints.channels ||
206 m_hints_current.samplerate != m_hints.samplerate ||
207 m_hints_current.bitspersample != m_hints.bitspersample ||
208 old_bitrate != new_bitrate ||
211 m_hints_current = m_hints;
218 bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
220 if(!pkt || m_bad_state || !m_pAudioCodec)
223 if(pkt->dts != DVD_NOPTS_VALUE)
224 m_audioClock = pkt->dts;
226 const uint8_t *data_dec = pkt->pData;
227 int data_len = pkt->iSize;
229 if(!OMX_IS_RAW(m_format.m_dataFormat) && !bDropPacket)
231 while(!m_bStop && data_len > 0)
233 int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len);
234 if( (len < 0) || (len > data_len) )
236 m_pAudioCodec->Reset();
244 int decoded_size = m_pAudioCodec->GetData(&decoded);
251 m_audioStats.AddSampleBytes(decoded_size);
255 m_DecoderOpen = OpenDecoder();
262 // discard if flushing as clocks may be stopped and we'll never submit it
266 if(m_omxAudio.GetSpace() < (unsigned int)decoded_size)
274 // Zero out the frame data if we are supposed to silence the audio
276 memset(decoded, 0x0, decoded_size);
278 ret = m_omxAudio.AddPackets(decoded, decoded_size, m_audioClock, m_audioClock);
280 if(ret != decoded_size)
282 CLog::Log(LOGERROR, "error ret %d decoded_size %d\n", ret, decoded_size);
291 else if(!bDropPacket)
295 m_DecoderOpen = OpenDecoder();
305 if(m_omxAudio.GetSpace() < (unsigned int)pkt->iSize)
314 memset(pkt->pData, 0x0, pkt->iSize);
316 m_omxAudio.AddPackets(pkt->pData, pkt->iSize, m_audioClock, m_audioClock);
319 m_audioStats.AddSampleBytes(pkt->iSize);
328 // signal to our parent that we have initialized
329 if(m_started == false)
332 m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO));
338 void OMXPlayerAudio::Process()
340 m_audioStats.Start();
345 int priority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
348 MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority);
350 if (ret == MSGQ_TIMEOUT)
356 if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT)
362 if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
364 DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
365 bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
368 CLog::Log(LOGINFO, "Audio: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d,%d", pPacket->dts, pPacket->pts,
369 (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, (int)m_omxAudio.GetAudioRenderingLatency(), (int)m_hints_current.samplerate);
371 if(Decode(pPacket, m_speed > DVD_PLAYSPEED_NORMAL || m_speed < 0 || bPacketDrop))
373 // we are not running until something is cached in output device
374 if(m_stalled && m_omxAudio.GetCacheTime() > 0.0)
376 CLog::Log(LOGINFO, "COMXPlayerAudio - Switching to normal playback");
381 else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
383 if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait( 100, SYNCSOURCE_AUDIO ))
384 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE");
386 m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */
388 else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
389 { //player asked us to set internal clock
390 CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
391 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, %d)", m_audioClock, pMsgGeneralResync->m_clock);
393 m_audioClock = DVD_NOPTS_VALUE;
395 else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
397 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESET");
399 m_pAudioCodec->Reset();
402 m_audioClock = DVD_NOPTS_VALUE;
404 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
406 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_FLUSH");
412 m_pAudioCodec->Reset();
413 m_audioClock = DVD_NOPTS_VALUE;
415 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
417 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::PLAYER_STARTED %d", m_started);
419 m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO));
421 else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME))
423 COMXPlayer::SPlayerState& state = ((CDVDMsgType<COMXPlayer::SPlayerState>*)pMsg)->m_value;
425 if(state.time_src == COMXPlayer::ETIMESOURCE_CLOCK)
426 state.time = DVD_TIME_TO_MSEC(m_av_clock->OMXMediaTime());
427 //state.time = DVD_TIME_TO_MSEC(m_av_clock->GetClock(state.timestamp) + state.time_offset);
429 state.timestamp = m_av_clock->GetAbsoluteClock();
430 state.player = DVDPLAYER_AUDIO;
431 m_messageParent.Put(pMsg->Acquire());
433 else if (pMsg->IsType(CDVDMsg::GENERAL_EOF))
435 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_EOF");
438 else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
440 double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
441 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_DELAY(%f)", timeout);
443 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
445 if (m_speed != static_cast<CDVDMsgInt*>(pMsg)->m_value)
447 m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
448 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::PLAYER_SETSPEED %d", m_speed);
451 else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE))
453 m_silence = static_cast<CDVDMsgBool*>(pMsg)->m_value;
455 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 1)", m_audioClock);
457 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 0)", m_audioClock);
459 else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
461 COMXMsgAudioCodecChange* msg(static_cast<COMXMsgAudioCodecChange*>(pMsg));
462 OpenStream(msg->m_hints, msg->m_codec);
470 void OMXPlayerAudio::Flush()
473 m_messageQueue.Flush();
474 m_messageQueue.Put( new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1);
477 void OMXPlayerAudio::WaitForBuffers()
479 // make sure there are no more packets available
480 m_messageQueue.WaitUntilEmpty();
482 // make sure almost all has been rendered
483 // leave 500ms to avound buffer underruns
484 double delay = GetCacheTime();
486 Sleep((int)(1000 * (delay - 0.5)));
489 bool OMXPlayerAudio::Passthrough() const
491 return m_passthrough;
494 AEDataFormat OMXPlayerAudio::GetDataFormat(CDVDStreamInfo hints)
496 AEDataFormat dataFormat = AE_FMT_S16NE;
497 bool hdmi_passthrough_dts = false;
498 bool hdmi_passthrough_ac3 = false;
500 if (m_DllBcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eAC3, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0)
501 hdmi_passthrough_ac3 = true;
502 if (m_DllBcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eDTS, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0)
503 hdmi_passthrough_dts = true;
505 m_passthrough = false;
508 /* check our audio capabilties */
510 /* pathrought is overriding hw decode*/
511 if(AUDIO_IS_BITSTREAM(CSettings::Get().GetInt("audiooutput.mode")) && m_use_passthrough)
513 if(hints.codec == AV_CODEC_ID_AC3 && CSettings::Get().GetBool("audiooutput.ac3passthrough") && hdmi_passthrough_ac3)
515 dataFormat = AE_FMT_AC3;
516 m_passthrough = true;
518 if(hints.codec == AV_CODEC_ID_DTS && CSettings::Get().GetBool("audiooutput.dtspassthrough") && hdmi_passthrough_dts)
520 dataFormat = AE_FMT_DTS;
521 m_passthrough = true;
526 if(m_use_hw_decode && !m_passthrough)
528 if(hints.codec == AV_CODEC_ID_AC3 && COMXAudio::CanHWDecode(m_hints.codec))
530 dataFormat = AE_FMT_AC3;
533 if(hints.codec == AV_CODEC_ID_DTS && COMXAudio::CanHWDecode(m_hints.codec))
535 dataFormat = AE_FMT_DTS;
541 if(!m_passthrough && !m_hw_decode)
543 if (m_pAudioCodec && m_pAudioCodec->GetBitsPerSample() == 16)
544 dataFormat = AE_FMT_S16NE;
546 dataFormat = AE_FMT_FLOAT;
552 bool OMXPlayerAudio::OpenDecoder()
554 m_nChannels = m_hints.channels;
555 m_passthrough = false;
561 m_omxAudio.Deinitialize();
562 m_DecoderOpen = false;
565 /* setup audi format for audio render */
566 m_format.m_sampleRate = m_hints.samplerate;
567 m_format.m_channelLayout = m_pAudioCodec->GetChannelMap();
568 /* GetDataFormat is setting up evrything */
569 m_format.m_dataFormat = GetDataFormat(m_hints);
571 std::string device = "";
573 if(CSettings::Get().GetInt("audiooutput.mode") == AUDIO_HDMI)
578 bool bAudioRenderOpen = m_omxAudio.Initialize(m_format, device, m_av_clock, m_hints, m_passthrough, m_hw_decode);
581 m_bad_state = !bAudioRenderOpen;
583 if(!bAudioRenderOpen)
585 CLog::Log(LOGERROR, "OMXPlayerAudio : Error open audio output");
586 m_omxAudio.Deinitialize();
590 CLog::Log(LOGINFO, "Audio codec %s channels %d samplerate %d bitspersample %d\n",
591 m_codec_name.c_str(), m_nChannels, m_hints.samplerate, m_hints.bitspersample);
596 return bAudioRenderOpen;
599 void OMXPlayerAudio::CloseDecoder()
601 m_omxAudio.Deinitialize();
602 m_DecoderOpen = false;
605 double OMXPlayerAudio::GetDelay()
607 return m_omxAudio.GetDelay();
610 double OMXPlayerAudio::GetCacheTime()
612 return m_omxAudio.GetCacheTime();
615 double OMXPlayerAudio::GetCacheTotal()
617 return m_omxAudio.GetCacheTotal();
620 void OMXPlayerAudio::SubmitEOS()
623 m_omxAudio.SubmitEOS();
626 bool OMXPlayerAudio::IsEOS()
628 return m_bad_state || m_omxAudio.IsEOS();
631 void OMXPlayerAudio::WaitCompletion()
633 unsigned int nTimeOut = AUDIO_BUFFER_SECONDS * 1000;
638 CLog::Log(LOGDEBUG, "%s::%s - got eos\n", CLASSNAME, __func__);
644 CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__);
652 void OMXPlayerAudio::RegisterAudioCallback(IAudioCallback *pCallback)
654 m_omxAudio.RegisterAudioCallback(pCallback);
657 void OMXPlayerAudio::UnRegisterAudioCallback()
659 m_omxAudio.UnRegisterAudioCallback();
662 bool OMXPlayerAudio::SetCurrentVolume(float fVolume)
664 return m_omxAudio.SetCurrentVolume(fVolume);
667 void OMXPlayerAudio::SetSpeed(int speed)
669 if(m_messageQueue.IsInited())
670 m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
675 int OMXPlayerAudio::GetAudioBitrate()
677 return (int)m_audioStats.GetBitrate();
680 std::string OMXPlayerAudio::GetPlayerInfo()
682 std::ostringstream s;
683 s << "aq:" << setw(2) << min(99,m_messageQueue.GetLevel() + MathUtils::round_int(100.0/8.0*GetCacheTime())) << "%";
684 s << ", Kb/s:" << fixed << setprecision(2) << (double)GetAudioBitrate() / 1024.0;