#define CEC_SERIAL_DEFAULT_BAUDRATE 38400
#define CEC_CLEAR_INPUT_DEFAULT_WAIT 1000
+#define CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS 5000
+
#define CEC_MIN_LIB_VERSION 1
#define CEC_LIB_VERSION_MAJOR 1
#define CEC_LIB_VERSION_MAJOR_STR "1"
using namespace PLATFORM;
#define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000
-#define ACTIVE_SOURCE_CHECK_TIMEOUT 15000
+#define ACTIVE_SOURCE_CHECK_INTERVAL 500
#define ToString(x) CCECTypeUtils::ToString(x)
it->second->ReplaceHandler(true);
}
-void CCECProcessor::CheckPendingActiveSource(void)
-{
- if (!CECInitialised())
- return;
-
- // check each device
- for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
- {
- if (it->second->GetHandler()->ActiveSourcePending())
- it->second->ActivateSource();
- it->second->MarkHandlerReady();
- }
-}
-
bool CCECProcessor::OnCommandReceived(const cec_command &command)
{
return m_inBuffer.Push(command);
m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
cec_command command;
- CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_TIMEOUT);
+ CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_INTERVAL);
// as long as we're not being stopped and the connection is open
while (!IsStopped() && m_communication->IsOpen())
// check whether we need to activate a source, if it failed before
if (activeSourceCheck.TimeLeft() == 0)
{
- CheckPendingActiveSource();
- activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_TIMEOUT);
+ if (CECInitialised())
+ TransmitPendingActiveSourceCommands();
+ activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_INTERVAL);
}
}
}
void SetCECInitialised(bool bSetTo = true);
void ReplaceHandlers(void);
- void CheckPendingActiveSource(void);
bool PhysicalAddressInUse(uint16_t iPhysicalAddress);
bool SetAckMask(uint16_t iMask);
bool CCECBusDevice::TransmitPendingActiveSourceCommands(void)
{
MarkBusy();
- bool bReturn = m_handler->TransmitPendingActiveSourceCommands();
+ bool bReturn = m_handler->ActivateSource(true);
MarkReady();
return bReturn;
}
m_bOPTSendDeckStatusUpdateOnActiveSource(false),
m_vendorId(CEC_VENDOR_UNKNOWN),
m_waitForResponse(new CWaitForResponse),
- m_bActiveSourcePending(false)
+ m_iActiveSourcePending(0)
{
}
return bReturn;
}
-bool CCECCommandHandler::ActivateSource(void)
+bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = false */)
{
if (m_busDevice->IsActiveSource() &&
- m_busDevice->IsHandledByLibCEC())
+ m_busDevice->IsHandledByLibCEC())
{
{
CLockObject lock(m_mutex);
- m_bActiveSourcePending = false;
+ // check if we need to send a delayed source switch
+ if (bTransmitDelayedCommandsOnly)
+ {
+ if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending)
+ return false;
+
+ LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
+ }
+
+ // clear previous pending active source command
+ m_iActiveSourcePending = 0;
}
+ // update the power state and menu state
m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
- m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED);
+ m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED); // TODO: LG
+
+ // power on the TV
+ bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
- bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn() ||
- !m_busDevice->TransmitActiveSource() ||
- !m_busDevice->TransmitMenuState(CECDEVICE_TV);
+ // check if we're allowed to switch sources
+ bool bSourceSwitchAllowed = SourceSwitchAllowed();
+ if (!bSourceSwitchAllowed)
+ LIB_CEC->AddLog(CEC_LOG_DEBUG, "source switch is currently not allowed by command handler");
- if (!bActiveSourceFailed)
+ // switch sources (if allowed)
+ if (!bActiveSourceFailed && bSourceSwitchAllowed)
{
- CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice();
- if (playbackDevice && SendDeckStatusUpdateOnActiveSource())
- bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV);
+ bActiveSourceFailed = !m_busDevice->TransmitActiveSource() ||
+ !m_busDevice->TransmitMenuState(CECDEVICE_TV);
+
+ // update the deck status for playback devices
+ if (!bActiveSourceFailed)
+ {
+ CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice();
+ if (playbackDevice && SendDeckStatusUpdateOnActiveSource())
+ bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV);
+ }
}
- if (bActiveSourceFailed)
+ // retry later
+ if (bActiveSourceFailed || !bSourceSwitchAllowed)
{
LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName());
CLockObject lock(m_mutex);
- m_bActiveSourcePending = true;
+ m_iActiveSourcePending = GetTimeMs() + CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
return false;
}
+ // mark the handler as initialised
+ CLockObject lock(m_mutex);
m_bHandlerInited = true;
}
return true;
{
m_waitForResponse->Received(opcode);
}
-
-bool CCECCommandHandler::ActiveSourcePending(void)
-{
- CLockObject lock(m_mutex);
- return m_bActiveSourcePending;
-}
static bool HasSpecificHandler(cec_vendor_id vendorId) { return vendorId == CEC_VENDOR_LG || vendorId == CEC_VENDOR_SAMSUNG || vendorId == CEC_VENDOR_PANASONIC;}
virtual bool InitHandler(void) { return true; }
- virtual bool ActivateSource(void);
+ virtual bool ActivateSource(bool bTransmitDelayedCommandsOnly = false);
virtual uint8_t GetTransmitRetries(void) const { return m_iTransmitRetries; }
virtual bool PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination);
virtual bool TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait = true);
virtual bool TransmitSetStreamPath(uint16_t iStreamPath);
virtual bool SendDeckStatusUpdateOnActiveSource(void) const { return m_bOPTSendDeckStatusUpdateOnActiveSource; };
- virtual bool TransmitPendingActiveSourceCommands(void) { return true; }
virtual void SignalOpcode(cec_opcode opcode);
- virtual bool ActiveSourcePending(void);
virtual bool SupportsDeviceType(const cec_device_type UNUSED(type)) const { return true; };
virtual cec_device_type GetReplacementDeviceType(const cec_device_type type) const { return type; }
virtual bool Transmit(cec_command &command, bool bSuppressWait = false);
+ virtual bool SourceSwitchAllowed(void) { return true; }
+
CCECBusDevice * m_busDevice;
CCECProcessor * m_processor;
int32_t m_iTransmitTimeout;
bool m_bOPTSendDeckStatusUpdateOnActiveSource;
cec_vendor_id m_vendorId;
CWaitForResponse *m_waitForResponse;
- bool m_bActiveSourcePending;
+ int m_iActiveSourcePending;
PLATFORM::CMutex m_mutex;
};
};
#include "VLCommandHandler.h"
#include "../devices/CECBusDevice.h"
+#include "../devices/CECPlaybackDevice.h"
+#include "../devices/CECTV.h"
#include "../CECProcessor.h"
#include "../LibCEC.h"
#include "../CECClient.h"
#define LIB_CEC m_busDevice->GetProcessor()->GetLib()
#define ToString(p) LIB_CEC->ToString(p)
+// wait this amount of ms before trying to switch sources after receiving the message from the TV that it's powered on
+#define SOURCE_SWITCH_DELAY_MS 1000
+
CVLCommandHandler::CVLCommandHandler(CCECBusDevice *busDevice) :
CCECCommandHandler(busDevice),
- m_bActiveSourcePending(false),
- m_bPowerUpEventReceived(false)
+ m_iPowerUpEventReceived(0)
{
m_vendorId = CEC_VENDOR_PANASONIC;
}
if (primary->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
return m_processor->GetPrimaryClient()->ChangeDeviceType(CEC_DEVICE_TYPE_RECORDING_DEVICE, CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
+
+ m_processor->GetTV()->RequestPowerStatus(primary->GetLogicalAddress(), false);
}
return CCECCommandHandler::InitHandler();
{
if (command.parameters.At(4) == VL_POWERED_UP)
{
- LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV powered up");
+ // set the power up event time
{
CLockObject lock(m_mutex);
- m_bPowerUpEventReceived = true;
+ if (m_iPowerUpEventReceived == 0)
+ m_iPowerUpEventReceived = GetTimeMs();
}
- m_processor->TransmitPendingActiveSourceCommands();
+ // mark the TV as powered on
+ m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON);
}
else if (command.parameters.At(4) == VL_POWERED_DOWN)
- LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV powered down");
- else if (command.parameters.At(4) == VL_POWERED_DOWN)
- LIB_CEC->AddLog(CEC_LOG_DEBUG, "unknown vendor command");
+ {
+ // reset the power up event time
+ {
+ CLockObject lock(m_mutex);
+ m_iPowerUpEventReceived = 0;
+ }
+ // mark the TV as powered off
+ m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_STANDBY);
+ }
+ else
+ LIB_CEC->AddLog(CEC_LOG_DEBUG, "skipping unknown vendor command");
return COMMAND_HANDLED;
}
return CCECCommandHandler::HandleDeviceVendorCommandWithId(command);
}
-bool CVLCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
+bool CVLCommandHandler::PowerUpEventReceived(void)
{
- bool bPowerUpEventReceived(false);
+ bool bPowerUpEventReceived(true);
- CCECBusDevice *tv = m_processor->GetDevice(CECDEVICE_TV);
- if (tv && tv->GetCurrentVendorId() == CEC_VENDOR_PANASONIC)
+ if (m_busDevice->GetLogicalAddress() != CECDEVICE_TV)
{
- CVLCommandHandler *handler = static_cast<CVLCommandHandler *>(tv->GetHandler());
- bPowerUpEventReceived = handler ? handler->PowerUpEventReceived() : false;
- tv->MarkHandlerReady();
- }
-
- if (!bPowerUpEventReceived)
- {
- CLockObject lock(m_mutex);
- // wait until we received the event
- m_bActiveSourcePending = true;
- return true;
+ // get the status from the TV
+ CCECBusDevice *tv = m_processor->GetTV();
+ if (tv && tv->GetCurrentVendorId() == CEC_VENDOR_PANASONIC)
+ {
+ CVLCommandHandler *handler = static_cast<CVLCommandHandler *>(tv->GetHandler());
+ bPowerUpEventReceived = handler ? handler->PowerUpEventReceived() : false;
+ tv->MarkHandlerReady();
+ }
}
else
{
- // transmit standard active source message
- return CCECCommandHandler::TransmitActiveSource(iInitiator, iPhysicalAddress);
- }
-}
-
-bool CVLCommandHandler::TransmitPendingActiveSourceCommands(void)
-{
- bool bTransmitCommand(false);
- {
- CLockObject lock(m_mutex);
- bTransmitCommand = m_bActiveSourcePending;
- m_bActiveSourcePending = false;
- }
-
- if (bTransmitCommand)
- {
- LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
- return CCECCommandHandler::TransmitActiveSource(m_busDevice->GetLogicalAddress(), m_busDevice->GetCurrentPhysicalAddress()) &&
- TransmitMenuState(m_busDevice->GetLogicalAddress(), CECDEVICE_TV, CEC_MENU_STATE_ACTIVATED);
- }
- return true;
-}
+ // get the current status
+ {
+ CLockObject lock(m_mutex);
+ bPowerUpEventReceived = m_iPowerUpEventReceived > 0 &&
+ GetTimeMs() - m_iPowerUpEventReceived > SOURCE_SWITCH_DELAY_MS;
+ }
-bool CVLCommandHandler::PowerUpEventReceived(void)
-{
- {
- CLockObject lock(m_mutex);
- if (m_bPowerUpEventReceived)
- return true;
+ // if we didn't receive the event, check if the TV is already marked as powered on
+ if (!bPowerUpEventReceived && m_busDevice->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON)
+ {
+ CLockObject lock(m_mutex);
+ m_iPowerUpEventReceived = GetTimeMs();
+ bPowerUpEventReceived = true;
+ }
}
- CLockObject lock(m_mutex);
- m_bPowerUpEventReceived = (m_busDevice->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON);
- return m_bPowerUpEventReceived;
+ return bPowerUpEventReceived;
}
int CVLCommandHandler::HandleStandby(const cec_command &command)
{
+ // reset the power up event time
{
CLockObject lock(m_mutex);
- m_bPowerUpEventReceived = false;
+ m_iPowerUpEventReceived = 0;
}
return CCECCommandHandler::HandleStandby(command);
return CEC_ABORT_REASON_INVALID_OPERAND;
}
+bool CVLCommandHandler::SourceSwitchAllowed(void)
+{
+ return PowerUpEventReceived();
+}
int HandleDeviceVendorCommandWithId(const cec_command &command);
int HandleStandby(const cec_command &command);
- bool TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress);
- bool TransmitPendingActiveSourceCommands(void);
int HandleVendorCommand(const cec_command &command);
bool PowerUpEventReceived(void);
bool SupportsDeviceType(const cec_device_type type) const { return type != CEC_DEVICE_TYPE_RECORDING_DEVICE; };
cec_device_type GetReplacementDeviceType(const cec_device_type type) const { return type == CEC_DEVICE_TYPE_RECORDING_DEVICE ? CEC_DEVICE_TYPE_PLAYBACK_DEVICE : type; }
+ bool SourceSwitchAllowed(void);
+
private:
PLATFORM::CMutex m_mutex;
- bool m_bActiveSourcePending;
- bool m_bPowerUpEventReceived;
+ uint64_t m_iPowerUpEventReceived;
};
};