/* * Copyright (C) 2011-2013 Team XBMC * http://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, see * . * */ #include "FileItem.h" #include "SlingboxFile.h" #include "File.h" #include "lib/SlingboxLib/SlingboxLib.h" #include "settings/Settings.h" #include "utils/log.h" #include "XMLUtils.h" using namespace XFILE; using namespace std; CSlingboxFile::CSlingboxFile() { // Create the Slingbox object m_pSlingbox = new CSlingbox(); } CSlingboxFile::~CSlingboxFile() { // Destroy the Slingbox object delete m_pSlingbox; } bool CSlingboxFile::Open(const CURL& url) { // Setup the IP/hostname and port (setup default port if none specified) unsigned int uiPort; if (url.HasPort()) uiPort = (unsigned int)url.GetPort(); else uiPort = 5001; m_pSlingbox->SetAddress(url.GetHostName(), uiPort); // Prepare to connect to the Slingbox bool bAdmin; if (url.GetUserName().CompareNoCase("administrator") == 0) bAdmin = true; else if (url.GetUserName().CompareNoCase("viewer") == 0) bAdmin = false; else { CLog::Log(LOGERROR, "%s - Invalid or no username specified for Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); return false; } // Connect to the Slingbox if (m_pSlingbox->Connect(bAdmin, url.GetPassWord())) { CLog::Log(LOGDEBUG, "%s - Sucessfully connected to Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); } else { CLog::Log(LOGERROR, "%s - Error connecting to Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); return false; } // Initialize the stream if (m_pSlingbox->InitializeStream()) { CLog::Log(LOGDEBUG, "%s - Successfully initialized stream on Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); } else { CLog::Log(LOGERROR, "%s - Error initializing stream on Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); return false; } // Set correct input if (url.GetFileNameWithoutPath() != "") { if (m_pSlingbox->SetInput(atoi(url.GetFileNameWithoutPath()))) CLog::Log(LOGDEBUG, "%s - Successfully requested change to input %i on Slingbox: %s", __FUNCTION__, atoi(url.GetFileNameWithoutPath()), url.GetHostName().c_str()); else CLog::Log(LOGERROR, "%s - Error requesting change to input %i on Slingbox: %s", __FUNCTION__, atoi(url.GetFileNameWithoutPath()), url.GetHostName().c_str()); } // Load the video settings LoadSettings(url.GetHostName()); // Setup video options if (m_pSlingbox->StreamSettings((CSlingbox::Resolution)m_sSlingboxSettings.iVideoResolution, m_sSlingboxSettings.iVideoBitrate, m_sSlingboxSettings.iVideoFramerate, m_sSlingboxSettings.iVideoSmoothing, m_sSlingboxSettings.iAudioBitrate, m_sSlingboxSettings.iIFrameInterval)) { CLog::Log(LOGDEBUG, "%s - Successfully set stream options (resolution: %ix%i; " "video bitrate: %i kbit/s; fps: %i; smoothing: %i%%; audio bitrate %i kbit/s; " "I frame interval: %i) on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.iVideoWidth, m_sSlingboxSettings.iVideoHeight, m_sSlingboxSettings.iVideoBitrate, m_sSlingboxSettings.iVideoFramerate, m_sSlingboxSettings.iVideoSmoothing, m_sSlingboxSettings.iAudioBitrate, m_sSlingboxSettings.iIFrameInterval, url.GetHostName().c_str()); } else { CLog::Log(LOGERROR, "%s - Error setting stream options on Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); } // Start the stream if (m_pSlingbox->StartStream()) { CLog::Log(LOGDEBUG, "%s - Successfully started stream on Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); } else { CLog::Log(LOGERROR, "%s - Error starting stream on Slingbox: %s", __FUNCTION__, url.GetHostName().c_str()); return false; } // Check for correct input if (url.GetFileNameWithoutPath() != "") { if (m_pSlingbox->GetInput() == -1) CLog::Log(LOGDEBUG, "%s - Unable to confirm change to input %i on Slingbox: %s", __FUNCTION__, atoi(url.GetFileNameWithoutPath()), url.GetHostName().c_str()); else if (m_pSlingbox->GetInput() == atoi(url.GetFileNameWithoutPath())) CLog::Log(LOGDEBUG, "%s - Comfirmed change to input %i on Slingbox: %s", __FUNCTION__, atoi(url.GetFileNameWithoutPath()), url.GetHostName().c_str()); else CLog::Log(LOGERROR, "%s - Error changing to input %i on Slingbox: %s", __FUNCTION__, atoi(url.GetFileNameWithoutPath()), url.GetHostName().c_str()); } return true; } unsigned int CSlingboxFile::Read(void * pBuffer, int64_t iSize) { // Read the data and check for any errors int iRead = m_pSlingbox->ReadStream(pBuffer, (unsigned int)iSize); if (iRead < 0) { CLog::Log(LOGERROR, "%s - Error reading stream from Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); return 0; } return iRead; } void CSlingboxFile::Close() { // Stop the stream if (m_pSlingbox->StopStream()) CLog::Log(LOGDEBUG, "%s - Successfully stopped stream on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); else CLog::Log(LOGERROR, "%s - Error stopping stream on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); // Disconnect from the Slingbox if (m_pSlingbox->Disconnect()) CLog::Log(LOGDEBUG, "%s - Successfully disconnected from Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); else CLog::Log(LOGERROR, "%s - Error disconnecting from Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } bool CSlingboxFile::SkipNext() { return m_pSlingbox->IsConnected(); } bool CSlingboxFile::NextChannel() { // Prepare variables bool bSuccess = true; int iPrevChannel = m_pSlingbox->GetChannel(); // Stop the stream if (m_pSlingbox->StopStream()) { CLog::Log(LOGDEBUG, "%s - Successfully stopped stream before channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error stopping stream before channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } // Figure out which method to use if (m_sSlingboxSettings.uiCodeChannelUp == 0) { // Change the channel if (m_pSlingbox->ChannelUp()) { CLog::Log(LOGDEBUG, "%s - Successfully requested channel change on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); if (m_pSlingbox->GetChannel() == -1) { CLog::Log(LOGDEBUG, "%s - Unable to confirm channel change on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else if (m_pSlingbox->GetChannel() != iPrevChannel) { CLog::Log(LOGDEBUG, "%s - Confirmed change to channel %i on Slingbox: %s", __FUNCTION__, m_pSlingbox->GetChannel(), m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error changing channel on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } else { CLog::Log(LOGERROR, "%s - Error requesting channel change on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } else { // Change the channel using IR command if (m_pSlingbox->SendIRCommand(m_sSlingboxSettings.uiCodeChannelUp)) { CLog::Log(LOGDEBUG, "%s - Successfully sent IR command (code: 0x%.2X) from " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.uiCodeChannelUp, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error sending IR command (code: 0x%.2X) from " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.uiCodeChannelUp, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } // Start the stream again if (m_pSlingbox->StartStream()) { CLog::Log(LOGDEBUG, "%s - Successfully started stream after channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error starting stream after channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } return bSuccess; } bool CSlingboxFile::PrevChannel() { // Prepare variables bool bSuccess = true; int iPrevChannel = m_pSlingbox->GetChannel(); // Stop the stream if (m_pSlingbox->StopStream()) { CLog::Log(LOGDEBUG, "%s - Successfully stopped stream before channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error stopping stream before channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } // Figure out which method to use if (m_sSlingboxSettings.uiCodeChannelDown == 0) { // Change the channel if (m_pSlingbox->ChannelDown()) { CLog::Log(LOGDEBUG, "%s - Successfully requested channel change on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); if (m_pSlingbox->GetChannel() == -1) { CLog::Log(LOGDEBUG, "%s - Unable to confirm channel change on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else if (m_pSlingbox->GetChannel() != iPrevChannel) { CLog::Log(LOGDEBUG, "%s - Confirmed change to channel %i on Slingbox: %s", __FUNCTION__, m_pSlingbox->GetChannel(), m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error changing channel on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } else { CLog::Log(LOGERROR, "%s - Error requesting channel change on Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } else { // Change the channel using IR command if (m_pSlingbox->SendIRCommand(m_sSlingboxSettings.uiCodeChannelDown)) { CLog::Log(LOGDEBUG, "%s - Successfully sent IR command (code: 0x%.2X) from " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.uiCodeChannelDown, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error sending IR command (code: 0x%.2X) from " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.uiCodeChannelDown, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } // Start the stream again if (m_pSlingbox->StartStream()) { CLog::Log(LOGDEBUG, "%s - Successfully started stream after channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error starting Slingbox stream after channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } return bSuccess; } bool CSlingboxFile::SelectChannel(unsigned int uiChannel) { // Check if a channel change is required if (m_pSlingbox->GetChannel() == uiChannel) return false; // Prepare variables bool bSuccess = true; // Stop the stream if (m_pSlingbox->StopStream()) { CLog::Log(LOGDEBUG, "%s - Successfully stopped stream before channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error stopping stream before channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } // Figure out which method to use unsigned int uiButtonsWithCode = 0; for (unsigned int i = 0; i < 10; i++) { if (m_sSlingboxSettings.uiCodeNumber[i] != 0) uiButtonsWithCode++; } if (uiButtonsWithCode == 0) { // Change the channel if (m_pSlingbox->SetChannel(uiChannel)) { CLog::Log(LOGDEBUG, "%s - Successfully requested change to channel %i on Slingbox: %s", __FUNCTION__, uiChannel, m_sSlingboxSettings.strHostname.c_str()); if (m_pSlingbox->GetChannel() == -1) { CLog::Log(LOGDEBUG, "%s - Unable to confirm change to channel %i on Slingbox: %s", __FUNCTION__, uiChannel, m_sSlingboxSettings.strHostname.c_str()); } else if (m_pSlingbox->GetChannel() == uiChannel) { CLog::Log(LOGDEBUG, "%s - Confirmed change to channel %i on Slingbox: %s", __FUNCTION__, uiChannel, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error changing to channel %i on Slingbox: %s", __FUNCTION__, uiChannel, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } else { CLog::Log(LOGERROR, "%s - Error requesting change to channel %i on Slingbox: %s", __FUNCTION__, uiChannel, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } else if (uiButtonsWithCode == 10) { // Prepare variables CStdString strDigits; strDigits.Format("%u", uiChannel); unsigned int uiNumberOfDigits = strDigits.GetLength(); // Change the channel using IR commands for (unsigned int i = 0; i < uiNumberOfDigits; i++) { if (m_pSlingbox->SendIRCommand(m_sSlingboxSettings.uiCodeNumber[strDigits[i] - '0'])) { CLog::Log(LOGDEBUG, "%s - Successfully sent IR command (code: 0x%.2X) from " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.uiCodeNumber[strDigits[i] - '0'], m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGDEBUG, "%s - Error sending IR command (code: 0x%.2X) from " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.uiCodeNumber[strDigits[i] - '0'], m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } } } else { CLog::Log(LOGERROR, "%s - Error requesting change to channel %i on Slingbox due to one or more " "missing button codes from advancedsettings.xml for Slingbox: %s", __FUNCTION__, uiChannel, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } // Start the stream again if (m_pSlingbox->StartStream()) { CLog::Log(LOGDEBUG, "%s - Successfully started stream after channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); } else { CLog::Log(LOGERROR, "%s - Error starting stream after channel change request on " "Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); bSuccess = false; } return bSuccess; } void CSlingboxFile::LoadSettings(const CStdString& strHostname) { // Load default settings m_sSlingboxSettings.strHostname = strHostname; m_sSlingboxSettings.iVideoWidth = 320; m_sSlingboxSettings.iVideoHeight = 240; m_sSlingboxSettings.iVideoResolution = (int)CSlingbox::RESOLUTION320X240; m_sSlingboxSettings.iVideoBitrate = 704; m_sSlingboxSettings.iVideoFramerate = 30; m_sSlingboxSettings.iVideoSmoothing = 50; m_sSlingboxSettings.iAudioBitrate = 64; m_sSlingboxSettings.iIFrameInterval = 10; m_sSlingboxSettings.uiCodeChannelUp = 0; m_sSlingboxSettings.uiCodeChannelDown = 0; for (unsigned int i = 0; i < 10; i++) m_sSlingboxSettings.uiCodeNumber[i] = 0; // Check if a SlingboxSettings.xml file exists CStdString slingboxXMLFile = g_settings.GetUserDataItem("SlingboxSettings.xml"); if (!CFile::Exists(slingboxXMLFile)) { CLog::Log(LOGNOTICE, "No SlingboxSettings.xml file (%s) found - using default settings", slingboxXMLFile.c_str()); return; } // Load the XML file TiXmlDocument slingboxXML; if (!slingboxXML.LoadFile(slingboxXMLFile)) { CLog::Log(LOGERROR, "%s - Error loading %s - line %d\n%s", __FUNCTION__, slingboxXMLFile.c_str(), slingboxXML.ErrorRow(), slingboxXML.ErrorDesc()); return; } // Check to make sure layout is correct TiXmlElement * pRootElement = slingboxXML.RootElement(); if (!pRootElement || strcmpi(pRootElement->Value(), "slingboxsettings") != 0) { CLog::Log(LOGERROR, "%s - Error loading %s - no node found", __FUNCTION__, slingboxXMLFile.c_str()); return; } // Success so far CLog::Log(LOGNOTICE, "Loaded SlingboxSettings.xml from %s", slingboxXMLFile.c_str()); // Search for the first settings that specify no hostname or match our hostname TiXmlElement *pElement; for (pElement = pRootElement->FirstChildElement("slingbox"); pElement; pElement = pElement->NextSiblingElement("slingbox")) { if (pElement->Attribute("hostname") == NULL || !m_sSlingboxSettings.strHostname.CompareNoCase(pElement->Attribute("hostname"))) { // Load setting values XMLUtils::GetInt(pElement, "width", m_sSlingboxSettings.iVideoWidth, 0, 640); XMLUtils::GetInt(pElement, "height", m_sSlingboxSettings.iVideoHeight, 0, 480); XMLUtils::GetInt(pElement, "videobitrate", m_sSlingboxSettings.iVideoBitrate, 50, 8000); XMLUtils::GetInt(pElement, "framerate", m_sSlingboxSettings.iVideoFramerate, 1, 30); XMLUtils::GetInt(pElement, "smoothing", m_sSlingboxSettings.iVideoSmoothing, 0, 100); XMLUtils::GetInt(pElement, "audiobitrate", m_sSlingboxSettings.iAudioBitrate, 16, 96); XMLUtils::GetInt(pElement, "iframeinterval", m_sSlingboxSettings.iIFrameInterval, 1, 30); // Load any button code values TiXmlElement * pCodes = pElement->FirstChildElement("buttons"); if (pCodes) { XMLUtils::GetHex(pCodes, "channelup", m_sSlingboxSettings.uiCodeChannelUp); XMLUtils::GetHex(pCodes, "channeldown", m_sSlingboxSettings.uiCodeChannelDown); XMLUtils::GetHex(pCodes, "zero", m_sSlingboxSettings.uiCodeNumber[0]); XMLUtils::GetHex(pCodes, "one", m_sSlingboxSettings.uiCodeNumber[1]); XMLUtils::GetHex(pCodes, "two", m_sSlingboxSettings.uiCodeNumber[2]); XMLUtils::GetHex(pCodes, "three", m_sSlingboxSettings.uiCodeNumber[3]); XMLUtils::GetHex(pCodes, "four", m_sSlingboxSettings.uiCodeNumber[4]); XMLUtils::GetHex(pCodes, "five", m_sSlingboxSettings.uiCodeNumber[5]); XMLUtils::GetHex(pCodes, "six", m_sSlingboxSettings.uiCodeNumber[6]); XMLUtils::GetHex(pCodes, "seven", m_sSlingboxSettings.uiCodeNumber[7]); XMLUtils::GetHex(pCodes, "eight", m_sSlingboxSettings.uiCodeNumber[8]); XMLUtils::GetHex(pCodes, "nine", m_sSlingboxSettings.uiCodeNumber[9]); } break; } } // Prepare our resolution enum mapping array const struct { unsigned int uiWidth; unsigned int uiHeight; CSlingbox::Resolution eEnum; } m_resolutionMap[11] = { {0, 0, CSlingbox::NOVIDEO}, {128, 96, CSlingbox::RESOLUTION128X96}, {160, 120, CSlingbox::RESOLUTION160X120}, {176, 120, CSlingbox::RESOLUTION176X120}, {224, 176, CSlingbox::RESOLUTION224X176}, {256, 192, CSlingbox::RESOLUTION256X192}, {320, 240, CSlingbox::RESOLUTION320X240}, {352, 240, CSlingbox::RESOLUTION352X240}, {320, 480, CSlingbox::RESOLUTION320X480}, {640, 240, CSlingbox::RESOLUTION640X240}, {640, 480, CSlingbox::RESOLUTION640X480} }; // See if the specified resolution matches something in our mapping array and // setup things accordingly for (unsigned int i = 0; i < 11; i++) { if (m_sSlingboxSettings.iVideoWidth == m_resolutionMap[i].uiWidth && m_sSlingboxSettings.iVideoHeight == m_resolutionMap[i].uiHeight) { m_sSlingboxSettings.iVideoResolution = (int)m_resolutionMap[i].eEnum; return; } } // If it didn't match anything setup safe defaults CLog::Log(LOGERROR, "%s - Defaulting to 320x240 resolution due to invalid " "resolution specified in SlingboxSettings.xml for Slingbox: %s", __FUNCTION__, m_sSlingboxSettings.strHostname.c_str()); m_sSlingboxSettings.iVideoWidth = 320; m_sSlingboxSettings.iVideoHeight = 240; m_sSlingboxSettings.iVideoResolution = (int)CSlingbox::RESOLUTION320X240; }