Readme.txt
Download File
Slingbox Library Readme
SlingboxLib is a full featured cross platform C++ library that allows communication with all the
various Slingbox devices made by Sling Media. It has been compiled and tested under Ubuntu 10.04,
FreeBSD 8.0, Mac OS X 10.5 for Power PC, Mac OS X 10.6 for Intel, 1st and 2nd Generation Apple TV,
and Windows XP, Vista, and 7.
Currently all Slingboxes encrypt their streams when resolution above 320x240 are selected. Some
Slingboxes, like the Slingbox Pro HD, encrypt all their streams, regardless of the selected
resolution.
SlingboxLib.h
Download File
//
// Copyright (C) 2010-2011 Stonyx
// http://www.stonyx.com
//
// This library is free software. You can redistribute it and/or modify it
// under the terms of the GNU General Public License Version 2 (or at your
// option any later version) as published by The Free Software Foundation.
//
// This library 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.
//
// If you did not received a copy of the GNU General Public License along
// with this library see http://www.gnu.org/copyleft/gpl.html or write to
// The Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <stdint.h>
#include <string.h>
#if defined _WIN32 || defined _WIN64
#include <winsock2.h>
#elif !defined SOCKET
typedef int SOCKET;
#endif
class CSlingbox
{
public:
// Enum that represents all the possible Slingbox resolutions
enum Resolution
{
NOVIDEO = 0x00000000,
RESOLUTION128X96 = 0x00000008,
RESOLUTION160X120 = 0x00000002,
RESOLUTION176X120 = 0x00000004,
RESOLUTION224X176 = 0x00000009,
RESOLUTION256X192 = 0x0000000B,
RESOLUTION320X240 = 0x00000001,
RESOLUTION352X240 = 0x00000003,
RESOLUTION320X480 = 0x00000007,
RESOLUTION640X240 = 0x00000006,
RESOLUTION640X480 = 0x00000005
};
// Constructors and destructors
CSlingbox();
CSlingbox(const char * szAddress, unsigned int uiPort = 5001);
~CSlingbox();
// Following function can be called instead of the SetAddress function and is used to
// find a Slingbox instead of manually setting address and port information
bool FindSlingbox(unsigned int uiTimeout = 10);
// Function used to retrive information about a found Slingbox
void GetAddress(char * szAddress, unsigned int uiAddressLength, unsigned int * uiPort);
// Following public functions are listed in order of suggested call sequence
void SetAddress(const char * szAddress, unsigned int uiPort = 5001);
bool Connect(bool bLoginAsAdmin, const char * szPassword);
bool InitializeStream();
bool StreamSettings(Resolution eResolution = RESOLUTION320X240,
uint32_t uiVideoBitrate = 704, uint32_t uiFrameRate = 30,
uint32_t uiVideoSmoothing = 50, uint32_t uiAudioBitrate = 64,
uint32_t uiIFrameInterval = 10);
bool StartStream();
int ReadStream(void * pBuffer, unsigned int uiSize);
bool StopStream();
bool Disconnect();
// Function used to find out if a connecton to the Slingbox is active
bool IsConnected();
// Set channel/input related functions can be called anytime after InitializeStream
// has succeeded
bool ChannelUp();
bool ChannelDown();
bool SetChannel(unsigned int uiChannel);
bool SetInput(unsigned int uiInput);
// Get channel function will usually return valid data after InitializeStream
// has succeeded
int GetChannel();
// Get input function will usually return valid data only when a stream is active
// (ie: after StartStream has succeeded and before StopStream is called)
int GetInput();
// Function used to send an IR command
bool SendIRCommand(uint8_t ucCommand);
protected:
// Function used to send and receive messages to and from the Slingbox
struct MessageHeader;
bool SendReceiveMessage(SOCKET socSocket, MessageHeader * pHeader,
bool bEncrypt = true, unsigned int uiTimeout = 10);
bool SendMessage(SOCKET socSocket, MessageHeader * pHeader,
bool bEncrypt = true, unsigned int uiTimeout = 10);
bool ReceiveMessage(SOCKET socSocket, bool bUDPMessage = false,
unsigned int uiTimeout = 10);
// Functions used to encode and decode data to and from the Slingbox
void Encode(void * pData, unsigned int uiSize);
void Decode(void * pData, unsigned int uiSize);
// Connection related functions
SOCKET OpenSocket(const char * szAddress, unsigned int uiPort, bool bUDP = false);
int Broadcast(SOCKET socSocket, unsigned int uiPort, void * pBuffer,
unsigned int uiSize, unsigned int uiTimeout = 10);
int Send(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout = 10);
int SendTo(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout, struct sockaddr * pSocketAddress);
int Receive(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout = 10);
int ReceiveFrom(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout, struct sockaddr * pSocketAddress);
bool CloseSocket(SOCKET socSocket);
// Function used to wait on the Slingbox for various reasons
void Wait(unsigned int uiMilliseconds);
// Protected member variables
SOCKET m_socCommunication;
SOCKET m_socStream;
char m_szAddress[1024];
unsigned int m_uiPort;
uint16_t m_usCode;
uint16_t m_usSequence;
int m_iChannel;
int m_iInput;
// Struct to define which messages were received
struct
{
bool bFindMessage;
bool bConnectMessage;
bool bInitializationMessage;
bool bEncryptionMessage;
bool bSettingsMessage;
bool bDisconnectMessage;
bool bStatusMessage;
bool bChannelMessage;
bool bInputMessage;
bool bChannelStatusMessage;
bool bInputStatusMessage;
bool bIRMessage;
} m_receivedMessages;
// Struct to define the Slingbox message header
struct MessageHeader
{
uint16_t m_usHeader; // Always 0x0101
uint16_t m_usCode; // Grabbed from the first packet from the Slingbox then
// always kept the same
uint16_t m_usMessageID; // Unique number to identify the message
uint16_t m_usVar4; // Always 0
uint16_t m_usSequence; // Sequencial number (answer will have the same number)
uint16_t m_usDirection; // 0 from Slingbox and 0x8000 from software
uint16_t m_usVar7;
uint16_t m_usVar8;
uint16_t m_usSize; // Size of the buffer (without header)
uint16_t m_usEncoded; // 0x2000 if buffer is encoded
uint16_t m_usVar11;
uint16_t m_usVar12;
uint16_t m_usVar13;
uint16_t m_usVar14;
uint16_t m_usVar15;
uint16_t m_usVar16;
// Struct constructor that sets all variables to default values
MessageHeader(uint16_t usMessageID, uint16_t usSize)
{
memset(this, 0, sizeof(MessageHeader));
m_usHeader = 0x0101;
m_usMessageID = usMessageID;
m_usSize = usSize - sizeof(MessageHeader);
}
};
// Struct to define the login message
struct ConnectMessage : public MessageHeader
{
uint32_t m_uiUnknown;
uint16_t m_usAccess[16];
uint16_t m_usPassword[16];
uint16_t m_usID[66];
// Struct constructor that sets variables to default values and copies
// passed values into variables
ConnectMessage(bool bLoginAsAdmin, const char * szPassword)
:MessageHeader(0x0067, sizeof(ConnectMessage))
{
m_uiUnknown = 0x00000000;
CopyCharToShort(m_usAccess, bLoginAsAdmin ? "admin" : "guest", 16);
CopyCharToShort(m_usPassword, szPassword, 16);
CopyCharToShort(m_usID, "Slingbox", 66);
}
// Function to copy a char array into a short array
void CopyCharToShort(uint16_t * usTarget, const char * szSource,
unsigned int uiTargetSize)
{
memset(usTarget, 0, uiTargetSize * sizeof(uint16_t));
for (unsigned int i = 0; i < uiTargetSize && szSource[i] != '\0'; i++)
{
usTarget[i] = (uint16_t)szSource[i];
}
}
};
// Struct to define the initialization message
struct InitializationMessage : public MessageHeader
{
uint32_t m_uiVar1;
uint32_t m_uiVar2;
// Struct constructor that sets all variables to default values
InitializationMessage()
:MessageHeader(0x007E, sizeof(InitializationMessage))
{
m_uiVar1 = 0x00000001;
m_uiVar2 = 0x00000000;
}
};
// Struct to define the encryption message
struct EncryptionMessage : public MessageHeader
{
uint32_t m_uiData[24];
// Struct constructor that sets all variables to default values
EncryptionMessage()
:MessageHeader(0x00A6, sizeof(EncryptionMessage))
{
memset(m_uiData, 0, 96);
m_uiData[0] = 0x00000100;
m_uiData[4] = 0x001D0000;
}
};
// Struct to define the settings message
struct SettingsMessage : public MessageHeader
{
uint32_t m_uiData[40];
// Struct constructor that sets variables to default values and copies
// passed values into variables
SettingsMessage(Resolution resolution, uint32_t uiVideoBitrate,
uint32_t uiFrameRate, uint32_t uiVideoSmoothing, uint32_t uiAudioBitrate,
uint32_t uiIFrameInterval)
:MessageHeader(0x00B5, sizeof(SettingsMessage))
{
// Make sure all our values are within limits
if (uiVideoBitrate < 50)
uiVideoBitrate = 50;
if (uiVideoBitrate > 8000)
uiVideoBitrate = 8000;
if (uiFrameRate < 1)
uiFrameRate = 1;
else if (uiFrameRate < 6)
uiFrameRate = 1;
else if (uiFrameRate < 10)
uiFrameRate = 6;
else if (uiFrameRate < 15)
uiFrameRate = 10;
else if (uiFrameRate < 20)
uiFrameRate = 15;
else if (uiFrameRate < 30)
uiFrameRate = 20;
else
uiFrameRate = 30;
if (uiVideoSmoothing > 100)
uiVideoSmoothing = 100;
if (uiAudioBitrate < 16)
uiAudioBitrate = 16;
else if (uiAudioBitrate < 20)
uiAudioBitrate = 16;
else if (uiAudioBitrate < 32)
uiAudioBitrate = 20;
else if (uiAudioBitrate < 40)
uiAudioBitrate = 32;
else if (uiAudioBitrate < 48)
uiAudioBitrate = 40;
else if (uiAudioBitrate < 64)
uiAudioBitrate = 48;
else if (uiAudioBitrate < 96)
uiAudioBitrate = 64;
else
uiAudioBitrate = 96;
if (uiIFrameInterval > 30)
uiIFrameInterval = 30;
// Set variables to default values and copy passed values into variables
m_uiData[0] = 0x000000FF;
m_uiData[1] = 0x000000FF;
m_uiData[2] = resolution;
m_uiData[3] = 0x00000001;
m_uiData[4] = uiVideoBitrate + (uiFrameRate << 16) +
(uiIFrameInterval << 24);
m_uiData[5] = 0x00000001 + (uiVideoSmoothing << 8);
m_uiData[6] = 0x00000003;
m_uiData[7] = 0x00000001;
m_uiData[8] = uiAudioBitrate;
m_uiData[9] = 0x00000003;
m_uiData[10] = 0x00000001;
m_uiData[11] = 0x46D4F252;
m_uiData[12] = 0x4C5D03E4;
m_uiData[13] = 0x04CA1F8D;
m_uiData[14] = 0xF1090089;
for (int i = 15; i < 40; i++)
m_uiData[i] = 0x00000000;
}
};
// Struct to define the disconnect message
struct DisconnectMessage : public MessageHeader
{
DisconnectMessage()
:MessageHeader(0x0068, sizeof(DisconnectMessage))
{
}
};
// Struct to define the status message
struct StatusMessage : public MessageHeader
{
StatusMessage()
:MessageHeader(0x0065, sizeof(StatusMessage))
{
}
};
// Struct to define the channel message
struct ChannelMessage : public MessageHeader
{
uint32_t m_uiUpDown; // 0 = up & 1 = down when channel = 0
uint32_t m_uiChannel;
uint32_t m_uiUnknown1;
uint32_t m_uiUnknown2;
// Struct constructor that sets variables to default values and copies
// passed values into variables
ChannelMessage(uint32_t uiChannel = 0)
:MessageHeader(0x0089, sizeof(ChannelMessage))
{
m_uiUpDown = 0x00000002;
m_uiChannel = uiChannel;
m_uiUnknown1 = 0xFF000000;
m_uiUnknown2 = 0x00000000;
}
// Function that sets variables to values required to change the channel up
void Up()
{
m_uiUpDown = 0x00000000;
m_uiChannel = 0x00000000;
m_uiUnknown1 = 0x000000FF;
m_uiUnknown2 = 0x00000000;
}
// Function that sets variables to values required to change the channel down
void Down()
{
m_uiUpDown = 0x00000001;
m_uiChannel = 0x00000000;
m_uiUnknown1 = 0x000000FF;
m_uiUnknown2 = 0x00000000;
}
};
// Struct to define the input message
struct InputMessage : public MessageHeader
{
uint8_t m_ucData[8];
// Struct constructor that sets variables to default values and copies
// passed values into variables
InputMessage(uint8_t ucInput)
:MessageHeader(0x0085, sizeof(InputMessage))
{
memset(m_ucData, 0, sizeof(m_ucData));
m_ucData[0] = ucInput;
}
};
// Struct to define the IR message
struct IRMessage : public MessageHeader
{
uint8_t m_ucData[480];
// Struct constructor that sets variables to default values and copies
// passed values into variables
IRMessage(uint8_t ucCode)
:MessageHeader(0x0087, sizeof(IRMessage))
{
memset(m_ucData, 0, sizeof(m_ucData));
m_ucData[0] = ucCode;
m_ucData[472] = 0xFF;
}
};
};
SlingboxLib.cpp
Download File
//
// Copyright (C) 2010-2011 Stonyx
// http://www.stonyx.com
//
// This library is free software. You can redistribute it and/or modify it
// under the terms of the GNU General Public License Version 2 (or at your
// option any later version) as published by The Free Software Foundation.
//
// This library 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.
//
// If you did not received a copy of the GNU General Public License along
// with this library see http://www.gnu.org/copyleft/gpl.html or write to
// The Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "SlingboxLib.h"
#if defined _WIN32 || defined _WIN64
#include <ws2tcpip.h>
typedef int socklen_t;
#else
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
#endif
// ********************
// Public Functions
// ********************
// Default constructor.
CSlingbox::CSlingbox()
{
// Set all variables to default/invalid values
m_socCommunication = INVALID_SOCKET;
m_socStream = INVALID_SOCKET;
memset(m_szAddress, 0, sizeof(m_szAddress));
m_uiPort = 0;
m_usCode = 0;
m_usSequence = 0;
m_iChannel = -1;
m_iInput = -1;
memset(&m_receivedMessages, 0, sizeof(m_receivedMessages));
}
// Alternative constructor used to set the address (IP or hostname) and port of the
// Slingbox we want to connect to. If no port is specified default port 5001 is used.
CSlingbox::CSlingbox(const char * szAddress, unsigned int uiPort /* = 5001 */)
{
// Call default constructor
CSlingbox();
// Set address and port variables to passed values
SetAddress(szAddress, uiPort);
}
// Destructor.
CSlingbox::~CSlingbox()
{
// Close all connections if they are still open
if (m_socStream != INVALID_SOCKET)
CloseSocket(m_socStream);
if (m_socCommunication != INVALID_SOCKET)
CloseSocket(m_socCommunication);
}
// Function used to find a Slingbox. Address and port of found Slingbox can be
// retrieved with the GetAddress function afterwards.
bool CSlingbox::FindSlingbox(unsigned int uiTimeout /* = 10 */)
{
// Open a UDP connection
SOCKET socSocket = OpenSocket(NULL, 0, true);
if (socSocket != INVALID_SOCKET)
{
// Prepare and send data
uint32_t uiData[8];
memset(uiData, 0, sizeof(uiData));
uiData[0] = 0x00000101;
uiData[1] = 0x00000002;
if (Broadcast(socSocket, 5004, uiData, sizeof(uiData), uiTimeout) <= 0)
return false;
// Reset address and port variables
memset(m_szAddress, 0, sizeof(m_szAddress));
m_uiPort = 0;
// Give the Slingbox time to respond
Wait(250);
// Look for correct return message and properly set variables
if (!ReceiveMessage(socSocket, true, uiTimeout) ||
!m_receivedMessages.bFindMessage ||
strlen(m_szAddress) == 0 || m_uiPort == 0)
{
CloseSocket(socSocket);
return false;
}
// Close socket
CloseSocket(socSocket);
}
else
{
return false;
}
return true;
}
// Function used to retrieve the address and port of a Slingbox found with the
// FindSlingbox function.
void CSlingbox::GetAddress(char * szAddress, unsigned int uiAddressLength,
unsigned int * uiPort)
{
// Copy requested data into passed pointers
memset(szAddress, 0, uiAddressLength);
strncpy(szAddress, m_szAddress, uiAddressLength - 1);
*uiPort = m_uiPort;
}
// Function used to set the address (IP or hostname) and port of the Slingbox we
// want to connect to. If no port is specified default port 5001 is used.
void CSlingbox::SetAddress(const char * szAddress, unsigned int uiPort /* = 5001 */)
{
// Set address and port variables to passed values
strncpy(m_szAddress, szAddress, sizeof(m_szAddress) - 1);
m_uiPort = uiPort;
}
// Function used to login into the Slingbox.
bool CSlingbox::Connect(bool bLoginAsAdmin, const char * szPassword)
{
// Check if a communication connection is already open
if (m_socCommunication != INVALID_SOCKET)
return false;
#if defined _WIN32 || defined _WIN64
// Enable use of the Winsock DLL
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return false;
#endif
// Open the communication connection
m_socCommunication = OpenSocket(m_szAddress, m_uiPort);
if (m_socCommunication == INVALID_SOCKET)
{
#if defined _WIN32 || defined _WIN64
// Finish using the Winsock DLL
WSACleanup();
#endif
return false;
}
// Prepare and send string
char * szString = "GET /stream.asf HTTP/1.1\r\nAccept: */*\r\n"
"Pragma: Sling-Connection-Type=Control, Session-Id=0\r\n\r\n";
if (Send(m_socCommunication, (void *)szString, strlen(szString)) <= 0)
{
// Close the communication connection
CloseSocket(m_socCommunication);
m_socCommunication = INVALID_SOCKET;
#if defined _WIN32 || defined _WIN64
// Finish using the Winsock DLL
WSACleanup();
#endif
return false;
}
// Invalidate variables
m_usCode = 0;
m_usSequence = 0;
m_iChannel = -1;
m_iInput = -1;
// Prepare and send the connect message
ConnectMessage message(bLoginAsAdmin, szPassword);
if (!SendReceiveMessage(m_socCommunication, &message) ||
!m_receivedMessages.bConnectMessage)
{
// Close the communication connection
CloseSocket(m_socCommunication);
m_socCommunication = INVALID_SOCKET;
#if defined _WIN32 || defined _WIN64
// Finish using the Winsock DLL
WSACleanup();
#endif
return false;
}
// Check if we got a valid code
if (m_usCode == 0)
{
// Close the communication connection
CloseSocket(m_socCommunication);
m_socCommunication = INVALID_SOCKET;
#if defined _WIN32 || defined _WIN64
// Finish using the Winsock DLL
WSACleanup();
#endif
return false;
}
return true;
}
// Function used to send the initialization message and initialize the stream.
bool CSlingbox::InitializeStream()
{
// Prepare and send start message
InitializationMessage message;
return SendReceiveMessage(m_socCommunication, &message) &&
m_receivedMessages.bInitializationMessage;
}
// Function used to set the stream settings.
bool CSlingbox::StreamSettings(Resolution eResolution /* = RESOLUTION320X240 */,
uint32_t uiVideoBitrate /* = 704 */, uint32_t uiFrameRate /* = 30 */,
uint32_t uiVideoSmoothing /* = 50 */, uint32_t uiAudioBitrate /* = 64 */,
uint32_t uiIFrameInterval /* = 10 */)
{
// Check if a resolution was specified that requires encryption
if (eResolution == RESOLUTION320X480 ||
eResolution == RESOLUTION640X240 ||
eResolution == RESOLUTION640X480)
{
// Enable encryption to enable requested resolution
EncryptionMessage message;
if (!SendReceiveMessage(m_socCommunication, &message) ||
!m_receivedMessages.bEncryptionMessage)
return false;
}
// Create and send video message with all the stream details
SettingsMessage message(eResolution, uiVideoBitrate, uiFrameRate, uiVideoSmoothing,
uiAudioBitrate, uiIFrameInterval);
if (!SendReceiveMessage(m_socCommunication, &message) ||
!m_receivedMessages.bSettingsMessage)
return false;
// Give the Slingbox time to change settings
Wait(500);
return true;
}
// Function used to start the stream.
bool CSlingbox::StartStream()
{
// Check if a stream connection is already open
if (m_socStream != INVALID_SOCKET)
return false;
// Open a new connection
m_socStream = OpenSocket(m_szAddress, m_uiPort);
if (m_socStream != INVALID_SOCKET)
{
// Prepare and send string
char szString[128] = "GET /stream.asf HTTP/1.1\r\nAccept: */*\r\n"
"Pragma: Sling-Connection-Type=Stream, Session-Id=";
sprintf(&szString[strlen(szString)], "%u", m_usCode);
strcpy(&szString[strlen(szString)], "\r\n\r\n");
if (Send(m_socStream, (void *)szString, strlen(szString)) <= 0)
{
// Close the stream connection
CloseSocket(m_socStream);
m_socStream = INVALID_SOCKET;
return false;
}
// Invalidate channel and input variables
m_iChannel = -1;
m_iInput = -1;
// Give the Slingbox time to respond
Wait(250);
// Check for correct return message
if (!ReceiveMessage(m_socCommunication) ||
!(m_receivedMessages.bChannelStatusMessage ||
m_receivedMessages.bInputStatusMessage))
{
// Close the stream connection
CloseSocket(m_socStream);
m_socStream = INVALID_SOCKET;
return false;
}
}
else
{
return false;
}
return true;
}
// Function used to read the stream. Returns number of bytes actually received
// or -1 if an error occured.
int CSlingbox::ReadStream(void * pBuffer, unsigned int uiSize)
{
return Receive(m_socStream, pBuffer, uiSize);
}
// Function used to stop the stream.
bool CSlingbox::StopStream()
{
// Close the stream connection
bool bSuccess = CloseSocket(m_socStream);
m_socStream = INVALID_SOCKET;
// Invalidate channel and input variables
m_iChannel = -1;
m_iInput = -1;
return bSuccess;
}
// Function used to disconnect from the Slingbox
bool CSlingbox::Disconnect()
{
// Prepare variables
bool bSuccess = true;
// Prepare and send the disconnect message
DisconnectMessage message;
if (!SendReceiveMessage(m_socCommunication, &message) ||
!m_receivedMessages.bDisconnectMessage)
bSuccess = false;
// Close the stream connection if it's still active
if (m_socStream != INVALID_SOCKET && !StopStream())
bSuccess = false;
// Close the communication connection
if (!CloseSocket(m_socCommunication))
bSuccess = false;
m_socCommunication = INVALID_SOCKET;
#if defined _WIN32 || defined _WIN64
// Finish using the Winsock DLL
if (WSACleanup() != 0)
bSuccess = false;
#endif
// Invalidate variables
m_usCode = 0;
m_usSequence = 0;
m_iChannel = -1;
m_iInput = -1;
return bSuccess;
}
// Function used to check if a connection to the Slingbox is active
bool CSlingbox::IsConnected()
{
// Prepare and send status message
StatusMessage message;
return SendReceiveMessage(m_socCommunication, &message) &&
m_receivedMessages.bStatusMessage;
}
// Function used to change the channel up.
bool CSlingbox::ChannelUp()
{
// Prepare and send channel message
ChannelMessage message;
message.Up();
if (!SendMessage(m_socCommunication, &message))
return false;
// Invalidate channel variable
m_iChannel = -1;
// Give the Slingbox time to change things
Wait(1000);
// Check for return message
return ReceiveMessage(m_socCommunication) &&
m_receivedMessages.bChannelMessage;
}
// Function used to change the channel down.
bool CSlingbox::ChannelDown()
{
// Prepare and send channel message
ChannelMessage message;
message.Down();
if (!SendMessage(m_socCommunication, &message))
return false;
// Invalidate channel variable
m_iChannel = -1;
// Give the Slingbox time to change things
Wait(1000);
// Check for return message
return ReceiveMessage(m_socCommunication) &&
m_receivedMessages.bChannelMessage;
}
// Function used to set the channel.
bool CSlingbox::SetChannel(unsigned int uiChannel)
{
// Prepare and send channel message
ChannelMessage message(uiChannel);
if (!SendMessage(m_socCommunication, &message))
return false;
// Invalidate channel variable
m_iChannel = -1;
// Give the Slingbox time to change things
Wait(1000);
// Check for return message
return ReceiveMessage(m_socCommunication) &&
m_receivedMessages.bChannelMessage;
}
// Function used to set the input.
bool CSlingbox::SetInput(unsigned int uiInput)
{
// Prepare and send input message
InputMessage message(uiInput);
if (!SendMessage(m_socCommunication, &message))
return false;
// Invalidate input variable
m_iInput = -1;
// Give the Slingbox time to change things
Wait(1000);
// Check for return message
return ReceiveMessage(m_socCommunication) &&
m_receivedMessages.bInputMessage;
}
// Function used to get the current channel. Returns -1 if valid data is not
// available.
int CSlingbox::GetChannel()
{
return m_iChannel;
}
// Function used to get the current input. Returns -1 if valid data is not
// available.
int CSlingbox::GetInput()
{
return m_iInput;
}
// Function used to send IR commands.
bool CSlingbox::SendIRCommand(uint8_t ucCode)
{
// Prepare and send IR message
IRMessage message(ucCode);
if (!SendMessage(m_socCommunication, &message))
return false;
// Give the Slingbox time to send command
Wait(1000);
// Check for return message
return ReceiveMessage(m_socCommunication) &&
m_receivedMessages.bIRMessage;
}
// ********************
// Protected Functions
// ********************
// Function used to send a message and receive messages.
bool CSlingbox::SendReceiveMessage(SOCKET socSocket, MessageHeader * pHeader,
bool bEncrypt /* = true */, unsigned int uiTimeout /* = 10 */)
{
// Send message
if (!SendMessage(socSocket, pHeader, bEncrypt, uiTimeout))
return false;
// Give the Slingbox time to respond
Wait(250);
// Receive messages
if (!ReceiveMessage(socSocket, false, uiTimeout))
return false;
return true;
}
// Function used to send a messages to the Slingbox.
bool CSlingbox::SendMessage(SOCKET socSocket, MessageHeader * pHeader,
bool bEncode /* = true */, unsigned int uiTimeout /* = 10 */)
{
// Set message code and sequence numbers
pHeader->m_usCode = m_usCode;
pHeader->m_usSequence = m_usSequence;
m_usSequence++;
// Check if we need to encode the data from the message
if (bEncode)
{
void* pPointer = ((uint8_t *)pHeader) + sizeof(MessageHeader);
Encode(pPointer, pHeader->m_usSize);
pHeader->m_usEncoded = 0x2000;
}
// Send the message
if (Send(socSocket, pHeader, sizeof(MessageHeader) +
pHeader->m_usSize, uiTimeout) <= 0)
return false;
return true;
}
// Function used to receive messages from the Slingbox and process them.
bool CSlingbox::ReceiveMessage(SOCKET socSocket, bool bUDPMessage /* = false */,
unsigned int uiTimeout /* = 10 */)
{
// Prepare variables
bool bMessageReceived = false;
int iReceived;
int iProcessed = 0;
uint8_t ucBuffer[1024];
sockaddr sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
// Reset received messages struct
memset(&m_receivedMessages, 0, sizeof(m_receivedMessages));
// Get the data
if (bUDPMessage)
{
iReceived = ReceiveFrom(socSocket, ucBuffer, sizeof(ucBuffer), uiTimeout,
&sSocketAddress);
}
else
{
iReceived = Receive(socSocket, ucBuffer, sizeof(ucBuffer), uiTimeout);
}
// Check for errors
if (iReceived == SOCKET_ERROR)
return false;
// Loop until we've processed all received data
while (iProcessed < iReceived)
{
// Prepare pointer
MessageHeader * pHeader = (MessageHeader *)(ucBuffer + iProcessed);
// Make sure it's a message and we got enough data to work with
if (pHeader->m_usHeader == 0x0101 && (iReceived - iProcessed) >=
(int)(sizeof(MessageHeader) + pHeader->m_usSize))
{
// Signal that we've recevied a message
bMessageReceived = true;
// Decode the message data
if (pHeader->m_usEncoded == 0x2000)
{
Decode((uint8_t *)pHeader + sizeof(MessageHeader), pHeader->m_usSize);
pHeader->m_usEncoded = 0x0000;
}
// Find message
if (pHeader->m_usMessageID == 0x0002 && pHeader->m_usSize == 0x005C)
{
m_receivedMessages.bFindMessage = true;
// Remove the port information from the structure received from ReceiveFrom
if (sSocketAddress.sa_family == AF_INET)
((sockaddr_in *)&sSocketAddress)->sin_port = 0;
else if (sSocketAddress.sa_family == AF_INET6)
((sockaddr_in6 *)&sSocketAddress)->sin6_port = 0;
// Convert the address received from ReceiveFrom to a string
#if defined _WIN32 || defined _WIN64
unsigned long ulSizeOfAddress = sizeof(m_szAddress);
WSAAddressToString(&sSocketAddress, sizeof(sSocketAddress), NULL,
m_szAddress, &ulSizeOfAddress);
#else
socklen_t iSizeOfAddress = sizeof(m_szAddress);
if (sSocketAddress.sa_family == AF_INET)
inet_ntop(sSocketAddress.sa_family,
&((sockaddr_in *)&sSocketAddress)->sin_addr, m_szAddress, iSizeOfAddress);
else if (sSocketAddress.sa_family == AF_INET6)
inet_ntop(sSocketAddress.sa_family,
&((sockaddr_in6 *)&sSocketAddress)->sin6_addr, m_szAddress, iSizeOfAddress);
#endif
// Get the port information from the received data
m_uiPort = ucBuffer[120] + (ucBuffer[121] << 8);
}
// Connect message
else if (pHeader->m_usMessageID == 0x0067 && pHeader->m_usSize == 0x0008)
{
m_receivedMessages.bConnectMessage = true;
m_usCode = pHeader->m_usCode;
}
// Initialization message
else if (pHeader->m_usMessageID == 0x007E)
{
m_receivedMessages.bInitializationMessage = true;
}
// Encryption message
else if (pHeader->m_usMessageID == 0x00A6)
{
m_receivedMessages.bEncryptionMessage = true;
}
// Settings message
else if (pHeader->m_usMessageID == 0x00B5)
{
m_receivedMessages.bSettingsMessage = true;
}
// Disconnect message
else if (pHeader->m_usMessageID == 0x0068)
{
m_receivedMessages.bDisconnectMessage = true;
}
// Status message
else if (pHeader->m_usMessageID == 0x0065 && pHeader->m_usSize == 0x0000)
{
m_receivedMessages.bStatusMessage = true;
}
// Channel message
else if (pHeader->m_usMessageID == 0x0089)
{
m_receivedMessages.bChannelMessage = true;
}
// Channel status massage
else if (pHeader->m_usMessageID == 0x0065 && pHeader->m_usSize == 0x0078)
{
m_receivedMessages.bChannelStatusMessage = true;
m_iChannel = *(uint16_t *)((uint8_t *)pHeader + sizeof(MessageHeader));
}
// Input message
else if (pHeader->m_usMessageID == 0x0085)
{
m_receivedMessages.bInputMessage = true;
}
// Input status message
else if (pHeader->m_usMessageID == 0x0065 && pHeader->m_usSize == 0x0008)
{
m_receivedMessages.bInputStatusMessage = true;
m_iInput = *((uint8_t *)pHeader + sizeof(MessageHeader));
}
// IR message
else if (pHeader->m_usMessageID == 0x0087)
{
m_receivedMessages.bIRMessage = true;
}
// Increase our counter
iProcessed += sizeof(MessageHeader) + pHeader->m_usSize;
}
else
{
return bMessageReceived;
}
}
return bMessageReceived;
}
// Function used to encode data being send to the Slingbox.
void CSlingbox::Encode(void * pData, unsigned int iSize)
{
for (unsigned int i = 0; i < iSize; i += 8)
{
// Prepare variables
const uint32_t ulKey[] = {0xBCDEAAAA, 0x87FBBBBA, 0x7CCCCFFA, 0xDDDDAABC};
uint32_t ulVar1 = ((uint32_t *)&((uint8_t *)pData)[i])[0];
uint32_t ulVar2 = ((uint32_t *)&((uint8_t *)pData)[i])[1];
uint32_t ulVar3 = 0;
// Encode
for (unsigned int j = 0; j < 32; j++)
{
ulVar3 -= 0x61C88647;
ulVar1 += ((ulVar2 >> 5) + ulKey[1]) ^ ((ulVar2 << 4) + ulKey[0]) ^ (ulVar3 +
ulVar2);
ulVar2 += ((ulVar1 >> 5) + ulKey[3]) ^ ((ulVar1 << 4) + ulKey[2]) ^ (ulVar3 +
ulVar1);
}
// Finish up
((uint32_t *)&((uint8_t *)pData)[i])[0] = ulVar1;
((uint32_t *)&((uint8_t *)pData)[i])[1] = ulVar2;
}
}
// Function used to decode data being received from the Slingbox.
void CSlingbox::Decode(void * pData, unsigned int iSize)
{
for (unsigned int i = 0 ; i < iSize; i += 8 )
{
// Prepare variables
const uint32_t ulKey[] = {0xBCDEAAAA, 0x87FBBBBA, 0x7CCCCFFA, 0xDDDDAABC};
uint32_t ulVar1 = ((uint32_t *)&((uint8_t *)pData)[i])[0];
uint32_t ulVar2 = ((uint32_t *)&((uint8_t *)pData)[i])[1];
uint32_t ulVar3 = 0xC6EF3720;
// Decode
for (unsigned int j = 0; j < 32; j++)
{
ulVar2 -= ((ulVar1 >> 5) + ulKey[3]) ^ ((ulVar1 << 4) + ulKey[2]) ^ (ulVar3 +
ulVar1);
ulVar1 -= ((ulVar2 >> 5) + ulKey[1]) ^ ((ulVar2 << 4) + ulKey[0]) ^ (ulVar3 +
ulVar2);
ulVar3 += 0x61C88647;
}
// Finish up
((uint32_t *)&((uint8_t *)pData)[i])[0] = ulVar1;
((uint32_t *)&((uint8_t *)pData)[i])[1] = ulVar2;
}
}
// Function used to open a new socket.
SOCKET CSlingbox::OpenSocket(const char * szAddress, unsigned int uiPort,
bool bUDP /* = false */)
{
// Prepare needed variables
SOCKET socSocket = INVALID_SOCKET;
struct addrinfo * pAddressInfo;
struct addrinfo addressInfoHints;
struct addrinfo * pPointer;
memset(&addressInfoHints, 0, sizeof(addressInfoHints));
addressInfoHints.ai_family = bUDP ? AF_INET : AF_UNSPEC;
addressInfoHints.ai_socktype = bUDP ? SOCK_DGRAM : SOCK_STREAM;
addressInfoHints.ai_protocol = bUDP ? IPPROTO_UDP : IPPROTO_TCP;
addressInfoHints.ai_flags = bUDP ? AI_PASSIVE : 0;
char szPort[32];
sprintf(szPort, "%u", uiPort);
// Get address info
if (getaddrinfo(szAddress, szPort, &addressInfoHints, &pAddressInfo) != 0)
return INVALID_SOCKET;
// Loop thru all the received address info
for (pPointer = pAddressInfo; pPointer != NULL; pPointer = pPointer->ai_next)
{
// Open a new socket
socSocket = socket(pPointer->ai_family, pPointer->ai_socktype,
pPointer->ai_protocol);
if (socSocket != INVALID_SOCKET)
{
// Bind if we're using UDP
if (bUDP)
{
if (bind(socSocket, pAddressInfo->ai_addr, pAddressInfo->ai_addrlen) == 0)
{
break;
}
else
{
CloseSocket(socSocket);
socSocket = INVALID_SOCKET;
}
}
// Open a new connection if we're using TCP
else
{
if (connect(socSocket, pAddressInfo->ai_addr, pAddressInfo->ai_addrlen) == 0)
{
break;
}
else
{
CloseSocket(socSocket);
socSocket = INVALID_SOCKET;
}
}
}
}
// Free variables
freeaddrinfo(pAddressInfo);
return socSocket;
}
// Function used to broadcast data to the local subnet. Returns the number of bytes
// actually sent or -1 if an error occured.
int CSlingbox::Broadcast(SOCKET socSocket, unsigned int uiPort, void * pBuffer,
unsigned int uiSize, unsigned int uiTimeout /* = 10 */)
{
// Enable broadcasting
#if defined _WIN32 || defined _WIN64
char vBroadcast = '1';
#else
int vBroadcast = 1;
#endif
if (setsockopt(socSocket, SOL_SOCKET, SO_BROADCAST, &vBroadcast,
sizeof(vBroadcast)) != 0)
return SOCKET_ERROR;
// Prepare variable
struct sockaddr sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
((sockaddr_in *)&sSocketAddress)->sin_family = AF_INET;
#if defined _WIN32 || defined _WIN64
((sockaddr_in *)&sSocketAddress)->sin_addr.S_un.S_addr = htonl(0xFFFFFFFF);
#else
((sockaddr_in *)&sSocketAddress)->sin_addr.s_addr = htonl(0xFFFFFFFF);
#endif
((sockaddr_in *)&sSocketAddress)->sin_port = htons(uiPort);
// Send the data
return SendTo(socSocket, pBuffer, uiSize, uiTimeout, &sSocketAddress);
}
// Function used to send data. Returns the number of bytes actually sent
// or -1 if an error occured.
int CSlingbox::Send(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout /* = 10 */)
{
// Prepare variables
int iFDS = socSocket + 1;
fd_set sendFDS;
FD_ZERO(&sendFDS);
FD_SET(socSocket, &sendFDS);
timeval timeOut;
timeOut.tv_sec = uiTimeout;
timeOut.tv_usec = 0;
int iResult;
unsigned int uiBytesSent = 0;
unsigned int uiBytesLeft = uiSize;
// Loop until all data is sent
while (uiBytesSent < uiSize)
{
// Send the data once the socket is ready
if (select(iFDS, NULL, &sendFDS, NULL, &timeOut) > 0)
iResult = send(socSocket, (const char *)pBuffer + uiBytesSent, uiBytesLeft, 0);
else
return uiBytesSent;
// Check for any errors
if (iResult == SOCKET_ERROR)
return SOCKET_ERROR;
// Adjust variables
uiBytesSent += iResult;
uiBytesLeft -= iResult;
}
return uiBytesSent;
}
// Function used to send data to a specific address. Returns the number of bytes
// actually sent or -1 if an error occured.
int CSlingbox::SendTo(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout, struct sockaddr * pSocketAddress)
{
// Prepare variables
int iFDS = socSocket + 1;
fd_set sendFDS;
FD_ZERO(&sendFDS);
FD_SET(socSocket, &sendFDS);
timeval timeOut;
timeOut.tv_sec = uiTimeout;
timeOut.tv_usec = 0;
int iResult;
unsigned int uiBytesSent = 0;
unsigned int uiBytesLeft = uiSize;
// Loop until all data is sent
while (uiBytesSent < uiSize)
{
// Send the data once the socket is ready
if (select(iFDS, NULL, &sendFDS, NULL, &timeOut) > 0)
iResult = sendto(socSocket, (const char *)pBuffer + uiBytesSent, uiBytesLeft, 0,
pSocketAddress, sizeof(*pSocketAddress));
else
return uiBytesSent;
// Check for any errors
if (iResult == SOCKET_ERROR)
return SOCKET_ERROR;
// Adjust variables
uiBytesSent += iResult;
uiBytesLeft -= iResult;
}
return uiBytesSent;
}
// Function used to receive data. Returns the number of bytes actually received
// or -1 if an error occured.
int CSlingbox::Receive(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout /* = 10 */)
{
// Prepare needed variables
int iFDS = socSocket + 1;
fd_set readFDS;
FD_ZERO(&readFDS);
FD_SET(socSocket, &readFDS);
timeval timeValue;
timeValue.tv_sec = uiTimeout;
timeValue.tv_usec = 0;
// Do the actual receiving of data after waiting for socket to be readable
if (select(iFDS, &readFDS, NULL, NULL, &timeValue) > 0)
return recv(socSocket, (char *)pBuffer, uiSize, 0);
else
return 0;
}
// Function used to receive data and tell us from where. Returns the number of bytes
// actually received or -1 if an error occured. If successful, populates the
// pSocketAddress variable with details of where the data was received from.
int CSlingbox::ReceiveFrom(SOCKET socSocket, void * pBuffer, unsigned int uiSize,
unsigned int uiTimeout, struct sockaddr * pSocketAddress)
{
// Prepare needed variables
int iFDS = socSocket + 1;
fd_set readFDS;
FD_ZERO(&readFDS);
FD_SET(socSocket, &readFDS);
timeval timeValue;
timeValue.tv_sec = uiTimeout;
timeValue.tv_usec = 0;
socklen_t iSizeOfAddress = sizeof(*pSocketAddress);
// Do the actual receiving of data after waiting for socket to be readable
if (select(iFDS, &readFDS, NULL, NULL, &timeValue) > 0)
return recvfrom(socSocket, (char *)pBuffer, uiSize, 0, pSocketAddress,
&iSizeOfAddress);
else
return 0;
}
// Function used to close a socket connection.
bool CSlingbox::CloseSocket(SOCKET socSocket)
{
#if defined _WIN32 || defined _WIN64
if (closesocket(socSocket) != 0)
return false;
#else
if (close(socSocket) != 0)
return false;
#endif
return true;
}
// Function used to wait on the Slingbox for various reasons
void CSlingbox::Wait(unsigned int uiMilliseconds)
{
#if defined _WIN32 || defined _WIN64
Sleep(uiMilliseconds);
#else
struct timespec time;
time.tv_sec = uiMilliseconds / 1000;
time.tv_nsec = (uiMilliseconds % 1000) * 1000000;
while (nanosleep(&time, &time) == -1 && errno == EINTR &&
(time.tv_sec > 0 || time.tv_nsec > 0));
#endif
}