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"
45 #include "cores/AudioEngine/AEFactory.h"
50 class COMXMsgAudioCodecChange : public CDVDMsg
53 COMXMsgAudioCodecChange(const CDVDStreamInfo &hints, COMXAudioCodecOMX* codec)
54 : CDVDMsg(GENERAL_STREAMCHANGE)
58 ~COMXMsgAudioCodecChange()
62 COMXAudioCodecOMX *m_codec;
63 CDVDStreamInfo m_hints;
66 OMXPlayerAudio::OMXPlayerAudio(OMXClock *av_clock, CDVDMessageQueue& parent)
67 : CThread("OMXPlayerAudio")
68 , m_messageQueue("audio")
69 , m_messageParent(parent)
71 m_av_clock = av_clock;
73 m_speed = DVD_PLAYSPEED_NORMAL;
76 m_audioClock = DVD_NOPTS_VALUE;
77 m_buffer_empty = false;
79 m_DecoderOpen = false;
81 m_hints_current.Clear();
83 bool small_mem = g_RBP.GetArmMem() < 256;
84 m_messageQueue.SetMaxDataSize((small_mem ? 3:6) * 1024 * 1024);
86 m_messageQueue.SetMaxTimeSize(8.0);
87 m_passthrough = false;
88 m_use_hw_decode = false;
95 OMXPlayerAudio::~OMXPlayerAudio()
100 bool OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints)
104 COMXAudioCodecOMX *codec = new COMXAudioCodecOMX();
106 if(!codec || !codec->Open(hints))
108 CLog::Log(LOGERROR, "Unsupported audio codec");
109 delete codec; codec = NULL;
113 if(m_messageQueue.IsInited())
114 m_messageQueue.Put(new COMXMsgAudioCodecChange(hints, codec), 0);
117 OpenStream(hints, codec);
118 m_messageQueue.Init();
119 CLog::Log(LOGNOTICE, "Creating audio thread");
126 void OMXPlayerAudio::OpenStream(CDVDStreamInfo &hints, COMXAudioCodecOMX *codec)
128 SAFE_DELETE(m_pAudioCodec);
131 m_pAudioCodec = codec;
133 if(m_hints.bitspersample == 0)
134 m_hints.bitspersample = 16;
136 m_speed = DVD_PLAYSPEED_NORMAL;
137 m_audioClock = DVD_NOPTS_VALUE;
143 m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
144 m_use_hw_decode = g_advancedSettings.m_omxHWAudioDecode;
145 m_format.m_dataFormat = GetDataFormat(m_hints);
146 m_format.m_sampleRate = 0;
147 m_format.m_channelLayout = 0;
150 bool OMXPlayerAudio::CloseStream(bool bWaitForBuffers)
152 // wait until buffers are empty
153 if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
155 m_messageQueue.Abort();
160 m_messageQueue.End();
164 m_pAudioCodec->Dispose();
165 delete m_pAudioCodec;
166 m_pAudioCodec = NULL;
171 m_speed = DVD_PLAYSPEED_NORMAL;
177 void OMXPlayerAudio::OnStartup()
181 void OMXPlayerAudio::OnExit()
183 CLog::Log(LOGNOTICE, "thread end: OMXPlayerAudio::OnExit()");
186 bool OMXPlayerAudio::CodecChange()
188 unsigned int old_bitrate = m_hints.bitrate;
189 unsigned int new_bitrate = m_hints_current.bitrate;
193 m_hints.channels = m_pAudioCodec->GetChannels();
194 m_hints.samplerate = m_pAudioCodec->GetSampleRate();
197 /* only check bitrate changes on AV_CODEC_ID_DTS, AV_CODEC_ID_AC3, AV_CODEC_ID_EAC3 */
198 if(m_hints.codec != AV_CODEC_ID_DTS && m_hints.codec != AV_CODEC_ID_AC3 && m_hints.codec != AV_CODEC_ID_EAC3)
199 new_bitrate = old_bitrate = 0;
201 // for passthrough we only care about the codec and the samplerate
202 bool minor_change = m_hints_current.channels != m_hints.channels ||
203 m_hints_current.bitspersample != m_hints.bitspersample ||
204 old_bitrate != new_bitrate;
206 if(m_hints_current.codec != m_hints.codec ||
207 m_hints_current.samplerate != m_hints.samplerate ||
208 (!m_passthrough && minor_change) || !m_DecoderOpen)
210 m_hints_current = m_hints;
217 bool OMXPlayerAudio::Decode(DemuxPacket *pkt, bool bDropPacket)
219 if(!pkt || m_bad_state || !m_pAudioCodec)
222 if(pkt->dts != DVD_NOPTS_VALUE)
223 m_audioClock = pkt->dts;
225 const uint8_t *data_dec = pkt->pData;
226 int data_len = pkt->iSize;
228 if(!OMX_IS_RAW(m_format.m_dataFormat) && !bDropPacket)
230 while(!m_bStop && data_len > 0)
232 int len = m_pAudioCodec->Decode((BYTE *)data_dec, data_len);
233 if( (len < 0) || (len > data_len) )
235 m_pAudioCodec->Reset();
243 int decoded_size = m_pAudioCodec->GetData(&decoded);
250 m_audioStats.AddSampleBytes(decoded_size);
254 m_DecoderOpen = OpenDecoder();
261 // discard if flushing as clocks may be stopped and we'll never submit it
265 if(m_omxAudio.GetSpace() < (unsigned int)decoded_size)
273 // Zero out the frame data if we are supposed to silence the audio
275 memset(decoded, 0x0, decoded_size);
277 ret = m_omxAudio.AddPackets(decoded, decoded_size, m_audioClock, m_audioClock);
279 if(ret != decoded_size)
281 CLog::Log(LOGERROR, "error ret %d decoded_size %d\n", ret, decoded_size);
290 else if(!bDropPacket)
294 m_DecoderOpen = OpenDecoder();
304 if(m_omxAudio.GetSpace() < (unsigned int)pkt->iSize)
313 memset(pkt->pData, 0x0, pkt->iSize);
315 m_omxAudio.AddPackets(pkt->pData, pkt->iSize, m_audioClock, m_audioClock);
318 m_audioStats.AddSampleBytes(pkt->iSize);
327 // signal to our parent that we have initialized
328 if(m_started == false)
331 m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO));
337 void OMXPlayerAudio::Process()
339 m_audioStats.Start();
344 int priority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
347 MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority);
349 if (ret == MSGQ_TIMEOUT)
355 if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT)
361 if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
363 DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
364 bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
367 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,
368 (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, (int)m_omxAudio.GetAudioRenderingLatency(), (int)m_hints_current.samplerate);
370 if(Decode(pPacket, m_speed > DVD_PLAYSPEED_NORMAL || m_speed < 0 || bPacketDrop))
372 // we are not running until something is cached in output device
373 if(m_stalled && m_omxAudio.GetCacheTime() > 0.0)
375 CLog::Log(LOGINFO, "COMXPlayerAudio - Switching to normal playback");
380 else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
382 if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait( 100, SYNCSOURCE_AUDIO ))
383 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE");
385 m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */
387 else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
388 { //player asked us to set internal clock
389 CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
391 if (pMsgGeneralResync->m_clock && pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
393 CLog::Log(LOGDEBUG, "CDVDPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, %f, 1)", m_audioClock, pMsgGeneralResync->m_timestamp);
394 m_av_clock->Discontinuity(pMsgGeneralResync->m_timestamp);
397 CLog::Log(LOGDEBUG, "CDVDPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 0)", m_audioClock);
400 m_audioClock = DVD_NOPTS_VALUE;
402 else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
404 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_RESET");
406 m_pAudioCodec->Reset();
409 m_audioClock = DVD_NOPTS_VALUE;
411 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
413 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_FLUSH");
419 m_pAudioCodec->Reset();
420 m_audioClock = DVD_NOPTS_VALUE;
422 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
424 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::PLAYER_STARTED %d", m_started);
426 m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO));
428 else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME))
430 COMXPlayer::SPlayerState& state = ((CDVDMsgType<COMXPlayer::SPlayerState>*)pMsg)->m_value;
431 double pts = m_audioClock;
432 double stamp = m_av_clock->OMXMediaTime();
434 if(state.time_src == COMXPlayer::ETIMESOURCE_CLOCK)
435 state.time = stamp == 0.0 ? state.time : DVD_TIME_TO_MSEC(stamp + state.time_offset);
437 state.time = stamp == 0.0 || pts == DVD_NOPTS_VALUE ? state.time : state.time + DVD_TIME_TO_MSEC(stamp - pts);
438 state.timestamp = m_av_clock->GetAbsoluteClock();
439 state.player = DVDPLAYER_AUDIO;
440 m_messageParent.Put(pMsg->Acquire());
442 else if (pMsg->IsType(CDVDMsg::GENERAL_EOF))
444 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_EOF");
447 else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
449 double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
450 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_DELAY(%f)", timeout);
452 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
454 if (m_speed != static_cast<CDVDMsgInt*>(pMsg)->m_value)
456 m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
457 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::PLAYER_SETSPEED %d", m_speed);
460 else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE))
462 m_silence = static_cast<CDVDMsgBool*>(pMsg)->m_value;
464 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 1)", m_audioClock);
466 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, 0)", m_audioClock);
468 else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
470 COMXMsgAudioCodecChange* msg(static_cast<COMXMsgAudioCodecChange*>(pMsg));
471 CLog::Log(LOGDEBUG, "COMXPlayerAudio - CDVDMsg::GENERAL_STREAMCHANGE");
472 OpenStream(msg->m_hints, msg->m_codec);
480 void OMXPlayerAudio::Flush()
483 m_messageQueue.Flush();
484 m_messageQueue.Put( new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1);
487 void OMXPlayerAudio::WaitForBuffers()
489 // make sure there are no more packets available
490 m_messageQueue.WaitUntilEmpty();
492 // make sure almost all has been rendered
493 // leave 500ms to avound buffer underruns
494 double delay = GetCacheTime();
496 Sleep((int)(1000 * (delay - 0.5)));
499 bool OMXPlayerAudio::Passthrough() const
501 return m_passthrough;
504 AEDataFormat OMXPlayerAudio::GetDataFormat(CDVDStreamInfo hints)
506 AEDataFormat dataFormat = AE_FMT_S16NE;
508 m_passthrough = false;
511 /* check our audio capabilties */
513 /* pathrought is overriding hw decode*/
514 if(hints.codec == AV_CODEC_ID_AC3 && CAEFactory::SupportsRaw(AE_FMT_AC3) && !CSettings::Get().GetBool("audiooutput.dualaudio"))
516 dataFormat = AE_FMT_AC3;
517 m_passthrough = true;
519 if(hints.codec == AV_CODEC_ID_DTS && CAEFactory::SupportsRaw(AE_FMT_DTS) && !CSettings::Get().GetBool("audiooutput.dualaudio"))
521 dataFormat = AE_FMT_DTS;
522 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;
560 m_omxAudio.Deinitialize();
561 m_DecoderOpen = false;
564 /* setup audi format for audio render */
565 m_format.m_sampleRate = m_hints.samplerate;
566 /* GetDataFormat is setting up evrything */
567 m_format.m_dataFormat = GetDataFormat(m_hints);
569 uint64_t channelMap = 0;
570 if (m_pAudioCodec && !m_passthrough)
571 channelMap = m_pAudioCodec->GetChannelMap();
572 else if (m_passthrough)
573 // we just want to get the channel count right to stop OMXAudio.cpp rejecting stream
574 // the actual layout is not used
575 channelMap = (1<<m_nChannels)-1;
576 bool bAudioRenderOpen = m_omxAudio.Initialize(m_format, m_av_clock, m_hints, channelMap, m_passthrough, m_hw_decode);
579 m_bad_state = !bAudioRenderOpen;
581 if(!bAudioRenderOpen)
583 CLog::Log(LOGERROR, "OMXPlayerAudio : Error open audio output");
584 m_omxAudio.Deinitialize();
588 CLog::Log(LOGINFO, "Audio codec %s channels %d samplerate %d bitspersample %d\n",
589 m_codec_name.c_str(), m_nChannels, m_hints.samplerate, m_hints.bitspersample);
594 return bAudioRenderOpen;
597 void OMXPlayerAudio::CloseDecoder()
599 m_omxAudio.Deinitialize();
600 m_DecoderOpen = false;
603 double OMXPlayerAudio::GetDelay()
605 return m_omxAudio.GetDelay();
608 double OMXPlayerAudio::GetCacheTime()
610 return m_omxAudio.GetCacheTime();
613 double OMXPlayerAudio::GetCacheTotal()
615 return m_omxAudio.GetCacheTotal();
618 void OMXPlayerAudio::SubmitEOS()
621 m_omxAudio.SubmitEOS();
624 bool OMXPlayerAudio::IsEOS()
626 return m_bad_state || m_omxAudio.IsEOS();
629 void OMXPlayerAudio::WaitCompletion()
631 unsigned int nTimeOut = AUDIO_BUFFER_SECONDS * 1000;
636 CLog::Log(LOGDEBUG, "%s::%s - got eos\n", CLASSNAME, __func__);
642 CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__);
650 void OMXPlayerAudio::SetSpeed(int speed)
652 if(m_messageQueue.IsInited())
653 m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
658 int OMXPlayerAudio::GetAudioBitrate()
660 return (int)m_audioStats.GetBitrate();
663 std::string OMXPlayerAudio::GetPlayerInfo()
665 std::ostringstream s;
666 s << "aq:" << setw(2) << min(99,m_messageQueue.GetLevel() + MathUtils::round_int(100.0/8.0*GetCacheTime())) << "%";
667 s << ", Kb/s:" << fixed << setprecision(2) << (double)GetAudioBitrate() / 1024.0;