+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <string.h>
-#include <assert.h>
-//#include <stdlib.h>
-//#include <stdio.h>
-//#include <time.h>
-//#include <arpa/inet.h>
-
-#ifdef WIN32
-#include <winsock.h> // for htons
-#endif
-
-#include "AMFObject.h"
-#include "log.h"
-#include "rtmp.h"
-#include "bytes.h"
-
-RTMP_LIB::AMFObjectProperty RTMP_LIB::AMFObject::m_invalidProp;
-
-RTMP_LIB::AMFObjectProperty::AMFObjectProperty()
-{
- Reset();
-}
-
-RTMP_LIB::AMFObjectProperty::AMFObjectProperty(const std::string & strName, double dValue)
-{
- Reset();
-}
-
-RTMP_LIB::AMFObjectProperty::AMFObjectProperty(const std::string & strName, bool bValue)
-{
- Reset();
-}
-
-RTMP_LIB::AMFObjectProperty::AMFObjectProperty(const std::string & strName, const std::string & strValue)
-{
- Reset();
-}
-
-RTMP_LIB::AMFObjectProperty::AMFObjectProperty(const std::string & strName, const AMFObject & objValue)
-{
- Reset();
-}
-
-RTMP_LIB::AMFObjectProperty::~ AMFObjectProperty()
-{
-}
-
-const std::string &RTMP_LIB::AMFObjectProperty::GetPropName() const
-{
- return m_strName;
-}
-
-void RTMP_LIB::AMFObjectProperty::SetPropName(const std::string& strName)
-{
- m_strName = strName;
-}
-
-RTMP_LIB::AMFDataType RTMP_LIB::AMFObjectProperty::GetType() const
-{
- return m_type;
-}
-
-double RTMP_LIB::AMFObjectProperty::GetNumber() const
-{
- return m_dNumVal;
-}
-
-bool RTMP_LIB::AMFObjectProperty::GetBoolean() const
-{
- return m_dNumVal != 0;
-}
-
-const std::string &RTMP_LIB::AMFObjectProperty::GetString() const
-{
- return m_strVal;
-}
-
-const RTMP_LIB::AMFObject &RTMP_LIB::AMFObjectProperty::GetObject() const
-{
- return m_objVal;
-}
-
-bool RTMP_LIB::AMFObjectProperty::IsValid() const
-{
- return (m_type != AMF_INVALID);
-}
-
-int RTMP_LIB::AMFObjectProperty::Encode(char * pBuffer, int nSize) const
-{
- int nBytes = 0;
-
- if (m_type == AMF_INVALID)
- return -1;
-
- if (m_type != AMF_NULL && nSize < (int)m_strName.size() + (int)sizeof(short) + 1)
- return -1;
-
- if (m_type != AMF_NULL && !m_strName.empty())
- {
- nBytes += EncodeName(pBuffer);
- pBuffer += nBytes;
- nSize -= nBytes;
- }
-
- switch (m_type)
- {
- case AMF_NUMBER:
- if (nSize < 9)
- return -1;
- nBytes += RTMP_LIB::CRTMP::EncodeNumber(pBuffer, GetNumber());
- break;
-
- case AMF_BOOLEAN:
- if (nSize < 2)
- return -1;
- nBytes += RTMP_LIB::CRTMP::EncodeBoolean(pBuffer, GetBoolean());
- break;
-
- case AMF_STRING:
- if (nSize < (int)m_strVal.size() + (int)sizeof(short))
- return -1;
- nBytes += RTMP_LIB::CRTMP::EncodeString(pBuffer, GetString());
- break;
-
- case AMF_NULL:
- if (nSize < 1)
- return -1;
- *pBuffer = 0x05;
- nBytes += 1;
- break;
-
- case AMF_OBJECT:
- {
- int nRes = m_objVal.Encode(pBuffer, nSize);
- if (nRes == -1)
- return -1;
-
- nBytes += nRes;
- break;
- }
- default:
- Log(LOGERROR,"%s, invalid type. %d", __FUNCTION__, m_type);
- return -1;
- };
-
- return nBytes;
-}
-
-//* TODO AMF3
-#define AMF3_INTEGER_MAX 268435455
-#define AMF3_INTEGER_MIN -268435456
-
-int AMF3ReadInteger(const char *data, int32_t *val)
-{
- //LogHex(data, 4);
- int i=0;
-
- while(i<=2) { // handle first 3 bytes
- if(data[i] & 0x80) { // byte used
- (*val) <<= 7; // shift up
- (*val) |= (data[i] & 0x7f); // add bits
- i++;
- } else { break; }
- }
-
- if(i>2) { // use 4th byte, all 8bits
- (*val) <<= 8;
- (*val) |= data[3];
-
- // range check
- if((*val) > AMF3_INTEGER_MAX)
- (*val) -= (1<<29);
- } else { // use 7bits of last unparsed byte (0xxxxxxx)
- (*val) <<= 7;
- (*val) |= data[i];
- }
-
- //Log(LOGDEBUG, "%s, AMF3 integer: %d, size: %d", __FUNCTION__, *val, i>2 ? 4 : i+1);
-
- return i>2 ? 4 : i+1;
-}
-
-int AMF3ReadString(const char *data, char **pStr)
-{
- assert(pStr != 0);
-
- int32_t ref = 0;
- int len = AMF3ReadInteger(data, &ref);
- data += len;
-
- if((ref & 0x1) == 0) { // reference: 0xxx
- uint32_t refIndex = (ref >> 1);
- Log(LOGDEBUG, "%s, string reference, index: %d, not supported, ignoring!", refIndex);
- return len;
- } else {
- uint32_t nSize = (ref >> 1);
-
- //Log(LOGDEBUG, "AMF3 String, len: %d, data: |%s|", nSize, data);
-
- (*pStr) = new char[nSize+1];
- memcpy(*pStr, data, nSize);
- (*pStr)[nSize]=0;
-
- return len+nSize;
- }
- return len;
-}
-
-int RTMP_LIB::AMFObjectProperty::AMF3Decode(const char * pBuffer, int nSize, bool bDecodeName)
-{
- int nOriginalSize = nSize;
-
- if (nSize == 0 || !pBuffer) {
- Log(LOGDEBUG,"empty buffer/no buffer pointer!");
- return -1;
- }
-
- //Log(LOGDEBUG, "Decoding property:");
- //LogHex(pBuffer, nSize);
-
- // decode name
- if(bDecodeName) {
- char *name;
- int nRes = AMF3ReadString(pBuffer, &name);
-
- if(strlen(name) <= 0)
- return nRes;
-
- //Log(LOGDEBUG, "AMF3 Property name: |%s|, size: %d", name, strlen(name));
- m_strName = name;
- pBuffer += nRes;
- nSize -= nRes;
- }
-
- // decode
- uint8_t type = *pBuffer;
-
- nSize--;
- pBuffer++;
-
- switch(type)
- {
- case 0x00: // AMF3_UNDEFINED
- case 0x01: // AMF3_NULL
- //Log(LOGDEBUG, "AMF3_UNDEFINED/NULL");
- m_type = AMF_NULL;
- break;
- case 0x02: // AMF3_FALSE
- //Log(LOGDEBUG, "AMF3_FALSE");
- m_type = AMF_BOOLEAN;
- m_dNumVal = 0.0;
- break;
- case 0x03: // AMF3_TRUE
- //Log(LOGDEBUG, "AMF3_TRUE");
- m_type = AMF_BOOLEAN;
- m_dNumVal = 1.0;
- break;
- case 0x04: // AMF3_INTEGER
- {
- int32_t res = 0;
- int len = AMF3ReadInteger(pBuffer, &res);
- //Log(LOGDEBUG, "AMF3_INTEGER: %d", res);
-
- m_type = AMF_NUMBER;
-
- nSize -= len;
- m_dNumVal = (double)res;
- break;
- }
- case 0x0A: // AMF3_OBJECT
- {
- Log(LOGDEBUG, "AMF3_OBJECT");
-
- int nRes = m_objVal.AMF3Decode(pBuffer, nSize, true);
- if(nRes == -1)
- return -1;
- nSize -= nRes;
- m_type = AMF_OBJECT;
- break;
- }
- case 0x06: // AMF3_STRING
- {
- //Log(LOGDEBUG, "AMF3_STRING");
- //LogHex(pBuffer, nSize);
-
- char *str = 0;
- int len = AMF3ReadString(pBuffer, &str);
- //Log(LOGDEBUG, "AMF3_STRING: %s", str);
- m_strVal = str;
- delete [] str;
- m_type = AMF_STRING;
- nSize -= len;
- break;
- }
- case 0x0B: // AMF3_XML_STRING, not tested
- case 0x07: // AMF3_XML_DOC
- {
- Log(LOGDEBUG, "AMF3_XML_DOC");
-
- char *str = 0;
- int len = AMF3ReadString(pBuffer, &str);
- m_strVal = str;
- delete [] str;
- m_type = AMF_STRING;
- nSize -= len;
- break;
- }
- case 0x05: // AMF3_NUMBER
- Log(LOGDEBUG, "AMF3_NUMBER");
- if (nSize < 8)
- return -1;
- m_dNumVal = ReadNumber(pBuffer);
- nSize -= 8;
- m_type = AMF_NUMBER;
- break;
- case 0x08: // AMF3_DATE, not tested
- {
- int32_t res = 0;
- int len = AMF3ReadInteger(pBuffer, &res);
-
- nSize -= len;
- pBuffer += len;
-
- if((res & 0x1) == 0) { // reference
- uint32_t nIndex = (res >> 1);
- Log(LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex);
- } else {
- if(nSize < 8)
- return -1;
-
- m_dNumVal = ReadNumber(pBuffer);
- nSize -= 8;
- m_type = AMF_NUMBER;
- }
- break;
- }
- case 0x09: // AMF3_ARRAY
- case 0x0C: // AMF3_BYTE_ARRAY
- default:
- Log(LOGDEBUG,"%s - AMF3 unknown/unsupported datatype 0x%02x, @0x%08X", __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
- return -1;
- }
-
- return nOriginalSize - nSize;
-}
-//*/
-
-int RTMP_LIB::AMFObjectProperty::Decode(const char * pBuffer, int nSize, bool bDecodeName)
-{
- int nOriginalSize = nSize;
-
- if (nSize == 0 || !pBuffer) {
- Log(LOGDEBUG,"%s: Empty buffer/no buffer pointer!", __FUNCTION__);
- return -1;
- }
-
- if (bDecodeName && nSize < 4) { // at least name (length + at least 1 byte) and 1 byte of data
- Log(LOGDEBUG,"%s: Not enough data for decoding with name, less then 4 bytes!", __FUNCTION__);
- return -1;
- }
-
- if (bDecodeName)
- {
- unsigned short nNameSize = RTMP_LIB::CRTMP::ReadInt16(pBuffer);
- if (nNameSize > nSize - 2) {
- Log(LOGDEBUG,"%s: Name size out of range: namesize (%d) > len (%d) - 2", __FUNCTION__, nNameSize, nSize);
- return -1;
- }
-
- m_strName = RTMP_LIB::CRTMP::ReadString(pBuffer);
- nSize -= 2 + m_strName.size();
- pBuffer += 2 + m_strName.size();
-
- //Log(LOGDEBUG, "%s: Decoded name: %s", __FUNCTION__, m_strName.c_str());
- }
-
- if (nSize == 0) {
- return -1;
- }
-
- nSize--;
-
- switch (*pBuffer)
- {
- case 0x00: // AMF_NUMBER:
- if (nSize < 8)
- return -1;
- m_dNumVal = ReadNumber(pBuffer+1);
- nSize -= 8;
- m_type = AMF_NUMBER;
- break;
- case 0x01: // AMF_BOOLEAN:
- if (nSize < 1)
- return -1;
- m_dNumVal = (double)RTMP_LIB::CRTMP::ReadBool(pBuffer+1);
- nSize--;
- m_type = AMF_BOOLEAN;
- break;
- case 0x02: // AMF_STRING:
- {
- unsigned short nStringSize = RTMP_LIB::CRTMP::ReadInt16(pBuffer+1);
- //Log(LOGDEBUG, "Read string, len: %d\n", nStringSize);
- //LogHex(pBuffer, nSize);
-
- if (nSize < (long)nStringSize + 2)
- return -1;
- m_strVal = RTMP_LIB::CRTMP::ReadString(pBuffer+1);
- nSize -= (2 + nStringSize);
- m_type = AMF_STRING;
- break;
- }
- case 0x03: // AMF_OBJECT:
- {
- int nRes = m_objVal.Decode(pBuffer+1, nSize, true);
- if (nRes == -1)
- return -1;
- nSize -= nRes;
- m_type = AMF_OBJECT;
- break;
- }
- case 0x04: // AMF_MOVIE_CLIP
- {
- Log(LOGERROR, "AMF_MOVIE_CLIP not supported!");
- return -1;
- break;
- }
- case 0x07: // AMF_REFERENCE
- {
- Log(LOGERROR, "AMF_REFERENCE not supported!");
- return -1;
- break;
- }
- case 0x0A: // AMF_ARRAY
- {
- unsigned int nArrayLen = RTMP_LIB::CRTMP::ReadInt32(pBuffer+1);
- nSize -= 4;
-
- int nRes = m_objVal.DecodeArray(pBuffer+5, nSize, nArrayLen, false);
- if (nRes == -1)
- return -1;
- nSize -= nRes;
- m_type = AMF_OBJECT;
- break;
- }
- case 0x08: // AMF_MIXEDARRAY
- {
- //int nMaxIndex = RTMP_LIB::CRTMP::ReadInt32(pBuffer+1); // can be zero for unlimited
- nSize -= 4;
-
- // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object
- int nRes = m_objVal.Decode(pBuffer+5, nSize, true);
- if (nRes == -1)
- return -1;
- nSize -= nRes;
- m_type = AMF_OBJECT;
- break;
- }
- case 0x05: /* AMF_NULL */
- case 0x06: /* AMF_UNDEFINED */
- case 0x0D: /* AMF_UNSUPPORTED */
- m_type = AMF_NULL;
- break;
- case 0x0B: // AMF_DATE
- {
- Log(LOGDEBUG, "AMF_DATE");
-
- if (nSize < 10)
- return -1;
-
- m_dNumVal = ReadNumber(pBuffer+1);
- m_nUTCOffset = RTMP_LIB::CRTMP::ReadInt16(pBuffer+9);
-
- m_type = AMF_DATE;
- nSize -= 10;
- break;
- }
- case 0x0C: // AMF_LONG_STRING
- {
- Log(LOGWARNING, "AMF_LONG_STRING not tested!");
-
- unsigned int nStringSize = RTMP_LIB::CRTMP::ReadInt32(pBuffer+1);;
- if (nSize < (long)nStringSize + 4)
- return -1;
- m_strVal = RTMP_LIB::CRTMP::ReadString(pBuffer+1);
- nSize -= (4 + nStringSize);
- m_type = AMF_STRING;
- break;
- }
- case 0x0E: // AMF_RECORDSET
- {
- Log(LOGERROR, "AMF_RECORDSET not supported!");
- return -1;
- break;
- }
- case 0x0F: // AMF_XML
- {
- Log(LOGERROR, "AMF_XML not supported!");
- return -1;
- break;
- }
- case 0x10: //AMF_CLASS_OBJECT
- {
- Log(LOGERROR, "AMF_CLASS_OBJECT not supported!");
- return -1;
- break;
- }
- case 0x11: //AMF_AMF3_OBJECT
- {
- Log(LOGERROR, "AMF_AMF3_OBJECT to be tested!");
- int nRes = m_objVal.AMF3Decode(pBuffer+1, nSize, true);
- if (nRes == -1)
- return -1;
- nSize -= nRes;
- m_type = AMF_OBJECT;
- break;
- }
- default:
- Log(LOGDEBUG,"%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
- return -1;
- }
-
- return nOriginalSize - nSize;
-}
-
-void RTMP_LIB::AMFObjectProperty::Dump() const
-{
- if (m_type == AMF_INVALID)
- {
- Log(LOGDEBUG,"Property: INVALID");
- return;
- }
-
- if (m_type == AMF_NULL)
- {
- Log(LOGDEBUG,"Property: NULL");
- return;
- }
-
- if (m_type == AMF_OBJECT)
- {
- Log(LOGDEBUG,"Property: <Name: %25s, OBJECT>", m_strName.empty() ? "no-name." : m_strName.c_str());
- m_objVal.Dump();
- return;
- }
-
- char strRes[256]="";
- snprintf(strRes, 255, "Name: %25s, ", m_strName.empty()? "no-name.":m_strName.c_str());
-
- char str[256]="";
- switch(m_type)
- {
- case AMF_NUMBER:
- snprintf(str, 255, "NUMBER:\t%.2f", m_dNumVal);
- break;
- case AMF_BOOLEAN:
- snprintf(str, 255, "BOOLEAN:\t%s", m_dNumVal == 1.?"TRUE":"FALSE");
- break;
- case AMF_STRING:
- snprintf(str, 255, "STRING:\t%s", m_strVal.c_str());
- break;
- case AMF_DATE:
- snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d", m_dNumVal, m_nUTCOffset);
- break;
- default:
- snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)m_type);
- }
-
- Log(LOGDEBUG,"Property: <%s%s>", strRes, str);
-}
-
-void RTMP_LIB::AMFObjectProperty::Reset()
-{
- m_dNumVal = 0.;
- m_strVal.clear();
- m_objVal.Reset();
- m_type = AMF_INVALID;
-}
-
-int RTMP_LIB::AMFObjectProperty::EncodeName(char *pBuffer) const
-{
- short length = htons(m_strName.size());
- memcpy(pBuffer, &length, sizeof(short));
- pBuffer += sizeof(short);
-
- memcpy(pBuffer, m_strName.c_str(), m_strName.size());
- return m_strName.size() + sizeof(short);
-}
-
-
-// AMFObject
-
-RTMP_LIB::AMFObject::AMFObject()
-{
- Reset();
-}
-
-RTMP_LIB::AMFObject::~ AMFObject()
-{
- Reset();
-}
-
-int RTMP_LIB::AMFObject::Encode(char * pBuffer, int nSize) const
-{
- if (nSize < 4)
- return -1;
-
- *pBuffer = 0x03; // object
-
- int nOriginalSize = nSize;
- for (size_t i=0; i<m_properties.size(); i++)
- {
- int nRes = m_properties[i].Encode(pBuffer, nSize);
- if (nRes == -1)
- {
- Log(LOGERROR,"AMFObject::Encode - failed to encode property in index %d", i);
- }
- else
- {
- nSize -= nRes;
- pBuffer += nRes;
- }
- }
-
- if (nSize < 3)
- return -1; // no room for the end marker
-
- RTMP_LIB::CRTMP::EncodeInt24(pBuffer, 0x000009);
- nSize -= 3;
-
- return nOriginalSize - nSize;
-}
-
-int RTMP_LIB::AMFObject::DecodeArray(const char * pBuffer, int nSize, int nArrayLen, bool bDecodeName)
-{
- int nOriginalSize = nSize;
- bool bError = false;
-
- while(nArrayLen > 0)
- {
- nArrayLen--;
-
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.Decode(pBuffer, nSize, bDecodeName);
- if (nRes == -1)
- bError = true;
- else
- {
- nSize -= nRes;
- pBuffer += nRes;
- m_properties.push_back(prop);
- }
- }
- if (bError)
- return -1;
-
- return nOriginalSize - nSize;
-}
-
-int RTMP_LIB::AMFObject::AMF3Decode(const char * pBuffer, int nSize, bool bAMFData)
-{
- int nOriginalSize = nSize;
-
- if(bAMFData) {
- if(*pBuffer != 0x0A)
- Log(LOGERROR, "AMF3 Object encapsulated in AMF stream does not start with 0x0A!");
- pBuffer++;
- nSize--;
- }
-
- int32_t ref = 0;
- int len = AMF3ReadInteger(pBuffer, &ref);
- pBuffer += len;
- nSize -= len;
-
- if((ref & 1) == 0) { // object reference, 0xxx
- uint32_t objectIndex = (ref >> 1);
-
- Log(LOGDEBUG, "Object reference, index: %d", objectIndex);
- }
- else // object instance
- {
- int32_t classRef = (ref >> 1);
-
- AMF3ClassDefinition *classDef = 0;
-
- if((classRef & 0x1) == 0) { // class reference
- uint32_t classIndex = (classRef >> 1);
- Log(LOGDEBUG, "Class reference: %d", classIndex);
- } else {
- int32_t classExtRef = (classRef >> 1);
-
- bool bExternalizable = ( classExtRef & 0x1) == 1;
- bool bDynamic = ((classExtRef >> 1) & 0x1) == 1;
-
- uint32_t numMembers = classExtRef >> 2;
-
- // class name
- char *className = 0;
-
- len = AMF3ReadString(pBuffer, &className);
- nSize -= len;
- pBuffer += len;
-
- //std::string str = className;
-
- Log(LOGDEBUG, "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d", className, bExternalizable, bDynamic, numMembers);
- classDef = new AMF3ClassDefinition(className, bExternalizable, bDynamic);
- delete [] className;
-
- for(unsigned int i=0; i<numMembers; i++) {
- char *memberName = 0;
- len = AMF3ReadString(pBuffer, &memberName);
- Log(LOGDEBUG, "Member: %s", memberName);
- classDef->AddProperty(memberName);
- delete [] memberName;
- nSize -= len;
- pBuffer += len;
- }
- }
-
- // add as referencable object
- // ...
-
- if(classDef->isExternalizable()) {
- Log(LOGDEBUG, "Externalizable, TODO check");
-
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.AMF3Decode(pBuffer, nSize, false);
- if(nRes == -1)
- Log(LOGDEBUG, "%s, failed to decode AMF3 property!", __FUNCTION__);
- else {
- nSize -= nRes;
- pBuffer += nRes;
- }
-
- prop.SetPropName("DEFAULT_ATTRIBUTE");
- m_properties.push_back(prop);
- } else {
- for(int i=0; i<classDef->GetMemberCount(); i++) // non-dynamic
- {
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.AMF3Decode(pBuffer, nSize, false);
- if(nRes == -1)
- Log(LOGDEBUG, "%s, failed to decode AMF3 property!", __FUNCTION__);
-
- prop.SetPropName(classDef->GetProperty(i));
- //prop.Dump();
- m_properties.push_back(prop);
-
- pBuffer += nRes;
- nSize -= nRes;
- }
- if(classDef->isDynamic()) {
- int len = 0;
-
- do {
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.AMF3Decode(pBuffer, nSize, true);
-
- m_properties.push_back(prop);
-
- pBuffer += nRes;
- nSize -= nRes;
-
- len = prop.GetPropName().length();
- } while(len > 0);
-
- // property name
- /*
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.AMF3Decode(pBuffer, nSize);
- if (nRes == -1)
- Log(LOGDEBUG, "%s, failed to decode AMF3 property!", __FUNCTION__);
- m_properties.push_back(prop);*/
- }
- }
- Log(LOGDEBUG, "class object!");
- }
-
- /*while (nSize > 0) {
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.AMF3Decode(pBuffer, nSize, bDecodeName);
- if(nRes == -1)
- return -1;
-
- nSize -= nRes;
- pBuffer += nRes;
- //if(prop.GetType() != AMF_NULL)
- m_properties.push_back(prop);
- }*/
- return nOriginalSize - nSize;
-}
-
-int RTMP_LIB::AMFObject::Decode(const char * pBuffer, int nSize, bool bDecodeName)
-{
- int nOriginalSize = nSize;
- bool bError = false; // if there is an error while decoding - try to at least find the end mark 0x000009
-
- //Log(LOGDEBUG, "%s: size: %lu, %d", __FUNCTION__, nSize, bDecodeName);
-
- while (nSize >= 3)
- {
- if (RTMP_LIB::CRTMP::ReadInt24(pBuffer) == 0x000009)
- {
- nSize -= 3;
- bError = false;
- break;
- }
-
- if (bError)
- {
- Log(LOGERROR,"DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
- nSize--;
- pBuffer++;
- continue;
- }
-
- RTMP_LIB::AMFObjectProperty prop;
- int nRes = prop.Decode(pBuffer, nSize, bDecodeName);
- if (nRes == -1)
- bError = true;
- else
- {
- nSize -= nRes;
- pBuffer += nRes;
- m_properties.push_back(prop);
- }
- }
-
- if (bError)
- return -1;
-
- return nOriginalSize - nSize;
-}
-
-void RTMP_LIB::AMFObject::AddProperty(const AMFObjectProperty & prop)
-{
- m_properties.push_back(prop);
-}
-
-int RTMP_LIB::AMFObject::GetPropertyCount() const
-{
- return m_properties.size();
-}
-
-const RTMP_LIB::AMFObjectProperty & RTMP_LIB::AMFObject::GetProperty(const std::string & strName) const
-{
- for (size_t n=0; n<m_properties.size(); n++)
- {
- if (m_properties[n].GetPropName() == strName)
- return m_properties[n];
- }
-
- return m_invalidProp;
-}
-
-const RTMP_LIB::AMFObjectProperty & RTMP_LIB::AMFObject::GetProperty(size_t nIndex) const
-{
- if (nIndex >= m_properties.size())
- return m_invalidProp;
-
- return m_properties[nIndex];
-}
-
-void RTMP_LIB::AMFObject::Dump() const
-{
- //Log(LOGDEBUG,"START AMF Object Dump:");
-
- for (size_t n=0; n<m_properties.size(); n++) {
- m_properties[n].Dump();
- }
-
- //Log(LOGDEBUG,"END AMF Object Dump");
-}
-
-void RTMP_LIB::AMFObject::Reset()
-{
- m_properties.clear();
-}
-
-// AMF3ClassDefinition
-std::string strEmpty = "";
-
-RTMP_LIB::AMF3ClassDefinition::AMF3ClassDefinition(const std::string &strClassName, bool bExternalizable, bool bDynamic)
-{
- m_bExternalizable = bExternalizable;
- m_bDynamic = bDynamic;
- m_strClassName = strClassName;
-}
-
-RTMP_LIB::AMF3ClassDefinition::~AMF3ClassDefinition() {}
-
-void RTMP_LIB::AMF3ClassDefinition::AddProperty(const std::string &strPropertyName)
-{
- m_properties.push_back(strPropertyName);
-}
-
-const std::string & RTMP_LIB::AMF3ClassDefinition::GetProperty(size_t nIndex) const
-{
- if (nIndex >= m_properties.size())
- return strEmpty;
-
- return m_properties[nIndex];
-}
-
-int RTMP_LIB::AMF3ClassDefinition::GetMemberCount() const
-{
- return m_properties.size();
-}
-
+++ /dev/null
-#ifndef __AMF_OBJECT__H__
-#define __AMF_OBJECT__H__
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <string>
-#include <vector>
-#include <stdint.h>
-
-namespace RTMP_LIB
-{
- typedef enum {AMF_INVALID, AMF_NUMBER, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT, AMF_NULL, AMF_MIXEDARRAY, AMF_ARRAY, AMF_DATE } AMFDataType;
-
- class AMFObjectProperty;
- class AMFObject
- {
- public:
- AMFObject();
- virtual ~AMFObject();
-
- int Encode(char *pBuffer, int nSize) const;
- int AMF3Decode(const char * pBuffer, int nSize, bool bDecodeName=false);
- int Decode(const char *pBuffer, int nSize, bool bDecodeName=false);
- int DecodeArray(const char * pBuffer, int nSize, int nArrayLen, bool bDecodeName=false);
-
- void AddProperty(const AMFObjectProperty &prop);
-
- int GetPropertyCount() const;
- const AMFObjectProperty &GetProperty(const std::string &strName) const;
- const AMFObjectProperty &GetProperty(size_t nIndex) const;
-
- void Dump() const;
- void Reset();
- protected:
- static AMFObjectProperty m_invalidProp; // returned when no prop matches
- std::vector<AMFObjectProperty> m_properties;
- };
-
- class AMFObjectProperty
- {
- public:
- AMFObjectProperty();
- AMFObjectProperty(const std::string &strName, double dValue);
- AMFObjectProperty(const std::string &strName, bool bValue);
- AMFObjectProperty(const std::string &strName, const std::string &strValue);
- AMFObjectProperty(const std::string &strName, const AMFObject &objValue);
-
- virtual ~AMFObjectProperty();
-
- const std::string &GetPropName() const;
- void SetPropName(const std::string& strName);
-
- AMFDataType GetType() const;
-
- bool IsValid() const;
-
- double GetNumber() const;
- bool GetBoolean() const;
- const std::string &GetString() const;
- const AMFObject &GetObject() const;
-
- int Encode(char *pBuffer, int nSize) const;
- int AMF3Decode(const char * pBuffer, int nSize, bool bDecodeName=false);
- int Decode(const char *pBuffer, int nSize, bool bDecodeName);
-
- void Reset();
- void Dump() const;
- protected:
- int EncodeName(char *pBuffer) const;
-
- std::string m_strName;
-
- AMFDataType m_type;
- double m_dNumVal;
- int16_t m_nUTCOffset;
- AMFObject m_objVal;
- std::string m_strVal;
- };
-
- class AMF3ClassDefinition
- {
- public:
- AMF3ClassDefinition(const std::string &strClassName, bool bExternalizable, bool bDynamic);
- virtual ~AMF3ClassDefinition();
-
- void AddProperty(const std::string &strPropertyName);
- const std::string &GetProperty(size_t nIndex) const;
-
- int GetMemberCount() const;
-
- bool isExternalizable() { return m_bExternalizable; }
- bool isDynamic() { return m_bDynamic; }
- protected:
- std::string m_strClassName;
- bool m_bExternalizable;
- bool m_bDynamic;
-
- std::vector<std::string> m_properties;
- };
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- * Copyright (C) 2009 Howard Chu
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <assert.h>
-
-#ifdef WIN32
-#include <winsock.h>
-#define close(x) closesocket(x)
-#else
-#include <sys/times.h>
-#endif
-
-#include <openssl/sha.h>
-#include <openssl/hmac.h>
-#include <openssl/rc4.h>
-
-#include "rtmp.h"
-#include "AMFObject.h"
-#include "log.h"
-#include "bytes.h"
-
-#include "dh.h"
-
-#define RTMP_SIG_SIZE 1536
-#define RTMP_LARGE_HEADER_SIZE 12
-
-#define RTMP_BUFFER_CACHE_SIZE (16*1024) // needs to fit largest number of bytes recv() may return
-
-using namespace RTMP_LIB;
-using namespace std;
-
-const char GenuineFMSKey[] =
-{
- 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
- 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
-
- 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1,
- 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36,
- 0xcf, 0xeb, 0x31, 0xae
-}; // 68
-
-char GenuineFPKey[] =
-{
- 0x47,0x65,0x6E,0x75,0x69,0x6E,0x65,0x20,0x41,0x64,0x6F,0x62,0x65,0x20,0x46,0x6C,
- 0x61,0x73,0x68,0x20,0x50,0x6C,0x61,0x79,0x65,0x72,0x20,0x30,0x30,0x31,0xF0,0xEE,
- 0xC2,0x4A,0x80,0x68,0xBE,0xE8,0x2E,0x00,0xD0,0xD1,0x02,0x9E,0x7E,0x57,0x6E,0xEC,
- 0x5D,0x2D,0x29,0x80,0x6F,0xAB,0x93,0xB8,0xE6,0x36,0xCF,0xEB,0x31,0xAE
-}; // 62
-
-void InitRC4Encryption
-(
- uint8_t *secretKey,
- uint8_t *pubKeyIn,
- uint8_t *pubKeyOut,
- RC4_KEY **rc4keyIn,
- RC4_KEY **rc4keyOut
-)
-{
- uint8_t digest[SHA256_DIGEST_LENGTH];
- unsigned int digestLen = 0;
-
- *rc4keyIn = new RC4_KEY;
- *rc4keyOut = new RC4_KEY;
-
- HMAC_CTX ctx;
- HMAC_CTX_init(&ctx);
- HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0);
- HMAC_Update(&ctx, pubKeyIn, 128);
- HMAC_Final(&ctx, digest, &digestLen);
- HMAC_CTX_cleanup(&ctx);
-
- Log(LOGDEBUG, "RC4 Out Key: ");
- LogHex(LOGDEBUG, (char*)digest, 16);
-
- RC4_set_key(*rc4keyOut, 16, digest);
-
- HMAC_CTX_init(&ctx);
- HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0);
- HMAC_Update(&ctx, pubKeyOut, 128);
- HMAC_Final(&ctx, digest, &digestLen);
- HMAC_CTX_cleanup(&ctx);
-
- Log(LOGDEBUG, "RC4 In Key: ");
- LogHex(LOGDEBUG, (char*)digest, 16);
-
- RC4_set_key(*rc4keyIn, 16, digest);
-}
-/*
-void RC4Encrypt(char *src, char *dst, size_t len)
-{
- if(Link.rc4keyOut) {
- RC4(Link.rc4keyOut, len, (uint8_t*)src, (uint8_t*)dst);
- }
-}
-
-void RC4Decrypt(char *src, char *dst, size_t len)
-{
- if(Link.rc4keyIn) {
- RC4(Link.rc4keyIn, len, (uint8_t*)src, (uint8_t*)dst);
- }
-}
-*/
-
-unsigned int GetDHOffset2(char *handshake, unsigned int len)
-{
- unsigned int offset = 0;
- unsigned char *ptr = (unsigned char *)handshake + 768;
-
- assert(RTMP_SIG_SIZE <= len);
-
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr);
-
- unsigned int res = (offset % 632) + 8;
-
- if(res + 128 > 767) {
- Log(LOGERROR, "%s: Couldn't calculate correct DH offset (got %d), exiting!\n", __FUNCTION__, res);
- exit(1);
- }
- return res;
-}
-
-unsigned int GetDigestOffset2(char *handshake, unsigned int len)
-{
- unsigned int offset = 0;
- unsigned char *ptr = (unsigned char *)handshake + 772;
-
- //assert(12 <= len);
-
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr);
-
- unsigned int res = (offset % 728) + 776;
-
- if(res+32 > 1535) {
- Log(LOGERROR, "%s: Couldn't calculate correct digest offset (got %d), exiting\n", __FUNCTION__, res);
- exit(1);
- }
- return res;
-}
-
-unsigned int GetDHOffset1(char *handshake, unsigned int len)
-{
- unsigned int offset = 0;
- unsigned char *ptr = (unsigned char *)handshake + 1532;
-
- assert(RTMP_SIG_SIZE <= len);
-
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr);
-
- unsigned int res = (offset % 632) + 772;
-
- if(res+128 > 1531) {
- Log(LOGERROR, "%s: Couldn't calculate DH offset (got %d), exiting!\n", __FUNCTION__, res);
- exit(1);
- }
-
- return res;
-}
-
-unsigned int GetDigestOffset1(char *handshake, unsigned int len)
-{
- unsigned int offset = 0;
- unsigned char *ptr = (unsigned char *)handshake + 8;
-
- assert(12 <= len);
-
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr); ptr++;
- offset += (*ptr);
-
- unsigned int res = (offset % 728) + 12;
-
- if(res+32 > 771) {
- Log(LOGDEBUG, "%s: Couldn't calculate digest offset (got %d), exiting!\n", __FUNCTION__, res);
- exit(1);
- }
-
- return res;
-}
-
-void HMACsha256(const char *message, size_t messageLen, const char *key, size_t keylen, char *digest)
-{
- unsigned int digestLen;
-
- HMAC_CTX ctx;
- HMAC_CTX_init(&ctx);
- HMAC_Init_ex(&ctx, (unsigned char*)key, keylen, EVP_sha256(), NULL);
- HMAC_Update(&ctx, (unsigned char *)message, messageLen);
- HMAC_Final(&ctx, (unsigned char *)digest, &digestLen);
- HMAC_CTX_cleanup(&ctx);
-
- assert(digestLen == 32);
-}
-
-void CalculateDigest(unsigned int digestPos, char *handshakeMessage, const char *key, size_t keyLen, char *digest)
-{
- const int messageLen = RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH;
- char message[messageLen];
-
- memcpy(message, handshakeMessage, digestPos);
- memcpy(message+digestPos, &handshakeMessage[digestPos+SHA256_DIGEST_LENGTH], messageLen-digestPos);
-
- HMACsha256(message, messageLen, key, keyLen, digest);
-}
-
-bool VerifyDigest(unsigned int digestPos, char *handshakeMessage, const char *key, size_t keyLen)
-{
- char calcDigest[SHA256_DIGEST_LENGTH];
-
- CalculateDigest(digestPos, handshakeMessage, key, keyLen, calcDigest);
-
- return memcmp(&handshakeMessage[digestPos], calcDigest, SHA256_DIGEST_LENGTH)==0;
-}
-
-/* handshake
- *
- * Type = [1 bytes] 0x06, 0x08 encrypted, 0x03 plain
- * -------------------------------------------------------------------- [1536 bytes]
- * Uptime = [4 bytes] big endian unsigned number, uptime
- * Version = [4 bytes] each byte represents a version number, e.g. 9.0.124.0
- * ...
- *
- */
-
-bool CRTMP::HandShake(bool FP9HandShake)
-{
- bool encrypted = Link.protocol == RTMP_PROTOCOL_RTMPE || Link.protocol == RTMP_PROTOCOL_RTMPTE;
-
- if ( encrypted || Link.SWFHash )
- FP9HandShake = true;
- else
- FP9HandShake = false;
-
- char clientsig[RTMP_SIG_SIZE+1];
- char serversig[RTMP_SIG_SIZE];
-
- memset(clientsig, 0, RTMP_SIG_SIZE+1);
-
- Link.rc4keyIn = Link.rc4keyOut = 0;
-
- if(encrypted)
- clientsig[0] = 0x06; // 0x08 is RTMPE as well
- else
- clientsig[0] = 0x03;
-
-#if 0
- uint32_t uptime = htonl(GetTime());
- memcpy(clientsig + 1, &uptime, 4);
-#else
- clientsig[1] = 0;
- clientsig[2] = 0;
- clientsig[3] = 0;
- clientsig[4] = 0;
-#endif
-
- if(FP9HandShake) {
- //* TODO RTMPE ;), its just RC4 with diffie-hellman
- // set version to at least 9.0.115.0
- clientsig[5] = 9;
- clientsig[6] = 0;
- clientsig[7] = 124;
- clientsig[8] = 2;
-
- //Log(LOGDEBUG, "Client type: %02X\n", clientsig[0]);
- //clientsig[0] = 0x08;
- Log(LOGDEBUG, "%s: Client type: %02X\n", __FUNCTION__, clientsig[0]);
-
- //clientsig[0] = 0x08;
-
- /*clientsig[1] = 0x00;
- clientsig[2] = 0x00;
- clientsig[3] = 0x04;
- clientsig[4] = 0x60;
-
- clientsig[5] = 128;
- clientsig[6] = 0;
- clientsig[7] = 1;
- clientsig[8] = 2;
- clientsig[9] = 0xBE;
- clientsig[10] = 0xF6;
-
- //*/
- } else {
- memset(&clientsig[5], 0, 4);
- }
-
- // generate random data
-#ifdef _DEBUG
- for(int i=9; i<=RTMP_SIG_SIZE; i++)
- clientsig[i] = 0;//(char)(rand() % 256);//0xff;
-#else
- for(int i=9; i<=RTMP_SIG_SIZE; i++)
- clientsig[i] = (char)(rand() % 256);
-#endif
-
- int dhposClient = 0;
- RC4_KEY *keyIn = 0;
- RC4_KEY *keyOut = 0;
-
- if(encrypted) {
- // generate Diffie-Hellmann parameters
- Link.dh = DHInit(1024);
- if(!Link.dh) {
- Log(LOGERROR, "%s: Couldn't initialize Diffie-Hellmann!", __FUNCTION__);
- return false;
- }
-
- dhposClient = GetDHOffset1(&clientsig[1], RTMP_SIG_SIZE);
- Log(LOGDEBUG, "%s: DH pubkey position: %d", __FUNCTION__, dhposClient);
-
- if(!DHGenerateKey(Link.dh)) {
- Log(LOGERROR, "%s: Couldn't generate Diffie-Hellmann public key!", __FUNCTION__);
- return false;
- }
-
- if(!DHGetPublicKey(Link.dh, (uint8_t*)&clientsig[1+dhposClient], 128)) {
- Log(LOGERROR, "%s: Couldn't write public key!", __FUNCTION__);
- return false;
- }
- }
-
- // set handshake digest
- if(FP9HandShake)
- {
- int digestPosClient = GetDigestOffset1(clientsig+1, RTMP_SIG_SIZE); // maybe reuse this value in verification
- Log(LOGDEBUG, "%s: Client digest offset: %d", __FUNCTION__, digestPosClient);
-
- CalculateDigest(digestPosClient, clientsig+1, GenuineFPKey, 30, &clientsig[1+digestPosClient]);
-
- Log(LOGDEBUG, "%s: Initial client digest: ", __FUNCTION__);
- LogHex(LOGDEBUG, (char *)clientsig+1+digestPosClient, SHA256_DIGEST_LENGTH);
- }
-
- #ifdef _DEBUG
- Log(LOGDEBUG, "Clientsig: ");
- LogHex(LOGDEBUG, &clientsig[1], RTMP_SIG_SIZE);
- #endif
-
- if(!WriteN(clientsig, RTMP_SIG_SIZE + 1))
- return false;
-
- char type;
- if(ReadN(&type, 1) != 1) // 0x03 or 0x06
- return false;
-
- Log(LOGDEBUG, "%s: Type Answer : %02X", __FUNCTION__, type);
-
- if(type != clientsig[0])
- Log(LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d", __FUNCTION__, clientsig[0], type);
-
- if(ReadN(serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
- return false;
-
- // decode server response
- uint32_t suptime;
-
- memcpy(&suptime, serversig, 4);
- suptime = ntohl(suptime);
-
- Log(LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime);
- Log(LOGDEBUG, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__, serversig[4], serversig[5], serversig[6], serversig[7]);
-
- #ifdef _DEBUG
- Log(LOGDEBUG,"Server signature:");
- LogHex(LOGDEBUG, serversig, RTMP_SIG_SIZE);
- #endif
-
- if (!FP9HandShake) {
- if(!WriteN(serversig, RTMP_SIG_SIZE))
- return false;
- }
-
- // we have to use this signature now to find the correct algorithms for getting the digest and DH positions
- int digestPosServer = GetDigestOffset2(serversig, RTMP_SIG_SIZE);
- int dhposServer = GetDHOffset2(serversig, RTMP_SIG_SIZE);
-
- if(FP9HandShake && !VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36)) {
- Log(LOGWARNING, "Trying different position for server digest!\n");
- digestPosServer = GetDigestOffset1(serversig, RTMP_SIG_SIZE);
- dhposServer = GetDHOffset1(serversig, RTMP_SIG_SIZE);
-
- if(!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36)) {
- Log(LOGERROR, "Couldn't verify the server digest\n");//, continuing anyway, will probably fail!\n");
- return false;
- }
- }
-
- Log(LOGDEBUG, "%s: Server DH public key offset: %d", __FUNCTION__, dhposServer);
-
- // generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake)
- if(Link.SWFHash) {
- const char swfVerify[] = {0x01,0x01};
-
- memcpy(Link.SWFVerificationResponse, swfVerify, 2);
- EncodeInt32(&Link.SWFVerificationResponse[2], Link.SWFSize);
- EncodeInt32(&Link.SWFVerificationResponse[6], Link.SWFSize);
- HMACsha256(Link.SWFHash, SHA256_DIGEST_LENGTH, &serversig[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH, &Link.SWFVerificationResponse[10]);
- }
-
- // do Diffie-Hellmann Key exchange for encrypted RTMP
- if(encrypted) {
- // compute secret key
- uint8_t secretKey[128] = {0};
-
- //Log(LOGDEBUG, "Expecting secure key at %d\nKeys at ", dhposServer);
- //int i;
- //int len=0;
- int len = DHComputeSharedSecretKey(Link.dh, (uint8_t *)&serversig[dhposServer], 128, secretKey);
- if(len < 0) {
- Log(LOGDEBUG, "%s: Wrong secret key position!", __FUNCTION__);
- return false;
- }
-
- /*
- printf("sigpos: %d\n", sigpos);
- for(i=8; i<1535-128; i++) {
- if(i+128 < sigpos || i > sigpos+SHA256_DIGEST_LENGTH) {
- int len1 = DHComputeSharedSecretKey(Link.dh, (uint8_t *)&serversig[i], 128, secretKey);
- if(len1 > 0) {
- LogPrintf("%d,", i);
- //LogHex((char *)&serversig[i], 128);
- }
- }
- }
- LogPrintf("\n");//*/
-
- if(len < 0) {
- Log(LOGERROR, "%s: Couldn't compute secret key, the public key is probably insecure (FMS change?)\n", __FUNCTION__);
- exit(1);
- return false;
- }
-
- Log(LOGDEBUG, "%s: Secret key: ", __FUNCTION__);
- LogHex(LOGDEBUG, (char *)secretKey, 128);
-
- InitRC4Encryption(
- secretKey,
- (uint8_t*)&serversig[dhposServer],
- (uint8_t*)&clientsig[1+dhposClient],
- &keyIn,
- &keyOut);
-
- // well here is another interesting key, lets see what it is for!
- //HMACsha256(serversig, RTMP_SIG_SIZE, (char *)secretKey, 128, initialKey);
- //Log(LOGDEBUG, "%s: Calculated initial key:", __FUNCTION__);
- //LogHex(initialKey, SHA256_DIGEST_LENGTH);
- }
-
- // 2nd part of handshake
- char resp[RTMP_SIG_SIZE];
- if(ReadN(resp, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
- return false;
-
- #ifdef _DEBUG
- Log(LOGDEBUG, "%s: 2nd handshake: ", __FUNCTION__);
- LogHex(LOGDEBUG, resp, RTMP_SIG_SIZE);
- #endif
-
- if(FP9HandShake && resp[4] == 0 && resp[5] == 0 && resp[6] == 0 && resp[7] == 0) {
- Log(LOGDEBUG, "%s: Wait, did the server just refuse signed authetication?", __FUNCTION__);
- }
-
- if(!FP9HandShake) {
- if(memcmp(resp, clientsig + 1, RTMP_SIG_SIZE) != 0) {
- Log(LOGWARNING, "%s: client signature does not match!", __FUNCTION__);
- }
-
- } else {
- // verify server response
- int digestPosClient = GetDigestOffset1(clientsig+1, RTMP_SIG_SIZE);
-
- char signature[SHA256_DIGEST_LENGTH];
- char digest[SHA256_DIGEST_LENGTH];
-
- Log(LOGDEBUG, "%s: Client signature digest position: %d", __FUNCTION__, digestPosClient);
-
- HMACsha256(&clientsig[1+digestPosClient], SHA256_DIGEST_LENGTH, GenuineFMSKey, sizeof(GenuineFMSKey), digest);
- HMACsha256(resp, RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH, digest, SHA256_DIGEST_LENGTH, signature);
-
- // show some information
- Log(LOGDEBUG, "%s: Digest key: ", __FUNCTION__);
- LogHex(LOGDEBUG, digest, SHA256_DIGEST_LENGTH);
-
- Log(LOGDEBUG, "%s: Signature calculated:", __FUNCTION__);
- LogHex(LOGDEBUG, signature, SHA256_DIGEST_LENGTH);
-
- Log(LOGDEBUG, "%s: Server sent signature:", __FUNCTION__);
- LogHex(LOGDEBUG, &resp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH);
-
- if(memcmp(signature, &resp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH) != 0) {
- Log(LOGWARNING, "%s: Server not genuine Adobe!", __FUNCTION__);
- return false;
- } else {
- Log(LOGDEBUG, "%s: Genuine Adobe Flash Media Server", __FUNCTION__);
- }
-
- // generate signed answer
- char clientResp[RTMP_SIG_SIZE];
-#ifdef _DEBUG
- for(int i=0; i<RTMP_SIG_SIZE; i++)
- clientResp[i] = 0;//(char)(rand() % 256);//0xff;
-#else
- for(int i=0; i<RTMP_SIG_SIZE; i++)
- clientResp[i] = (char)(rand() % 256);
-#endif
-
- // calculate response now
- char signatureResp[SHA256_DIGEST_LENGTH];
- char digestResp[SHA256_DIGEST_LENGTH];
-
- HMACsha256(&serversig[digestPosServer], SHA256_DIGEST_LENGTH, GenuineFPKey, sizeof(GenuineFPKey), digestResp);
- HMACsha256(clientResp, RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH, digestResp, SHA256_DIGEST_LENGTH, signatureResp);
-
- // some info output
- Log(LOGDEBUG, "%s: Calculated digest key from secure key and server digest: ", __FUNCTION__);
- LogHex(LOGDEBUG, digestResp, SHA256_DIGEST_LENGTH);
-
- Log(LOGDEBUG, "%s: Client signature calculated:", __FUNCTION__);
- LogHex(LOGDEBUG, signatureResp, SHA256_DIGEST_LENGTH);
-
- memcpy(&clientResp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], signatureResp, SHA256_DIGEST_LENGTH);
-
- #ifdef _DEBUG
- Log(LOGDEBUG, "%s: Sending final signed handshake response: ", __FUNCTION__);
- LogHex(LOGDEBUG, clientResp, RTMP_SIG_SIZE);
- #endif
-
- if(!WriteN(clientResp, RTMP_SIG_SIZE))
- return false;
- }
-
- if(encrypted) {
- // set keys for encryption from now on
- Link.rc4keyIn = keyIn;
- Link.rc4keyOut = keyOut;
-
- char buff[RTMP_SIG_SIZE];
-
- // update the keystreams
- if(Link.rc4keyIn) {
- RC4(Link.rc4keyIn, RTMP_SIG_SIZE, (uint8_t*)buff, (uint8_t*)buff);
- }
-
- if(Link.rc4keyOut) {
- RC4(Link.rc4keyOut, RTMP_SIG_SIZE, (uint8_t*)buff, (uint8_t*)buff);
- }
- }
-
- Log(LOGDEBUG, "%s: Handshaking finished....", __FUNCTION__);
- return true;
-}
-
-
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- * Copyright (C) 2009 Howard Chu
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-
-#include <assert.h>
-
-#ifdef WIN32
-#include <winsock.h>
-#define close(x) closesocket(x)
-#else
-#include <sys/times.h>
-#endif
-
-#include "rtmp.h"
-#include "AMFObject.h"
-#include "log.h"
-#include "bytes.h"
-
-#define RTMP_SIG_SIZE 1536
-#define RTMP_LARGE_HEADER_SIZE 12
-
-#define RTMP_BUFFER_CACHE_SIZE (16*1024) // needs to fit largest number of bytes recv() may return
-
-using namespace RTMP_LIB;
-using namespace std;
-
-static const int packetSize[] = { 12, 8, 4, 1 };
-#define RTMP_PACKET_SIZE_LARGE 0
-#define RTMP_PACKET_SIZE_MEDIUM 1
-#define RTMP_PACKET_SIZE_SMALL 2
-#define RTMP_PACKET_SIZE_MINIMUM 3
-
-extern bool bCtrlC;
-
-int32_t GetTime()
-{
-#ifdef _DEBUG
- return 0;
-#elif defined(WIN32)
- return timeGetTime();
-#else
- struct tms t;
- return times(&t)*1000/sysconf(_SC_CLK_TCK);
-#endif
-}
-
-char RTMPProtocolStrings[][7] =
-{
- "RTMP",
- "RTMPT",
- "RTMPS",
- "RTMPE",
- "RTMPTE",
- "RTMFP"
-};
-
-char RTMPProtocolStringsLower[][7] =
-{
- "rtmp",
- "rtmpt",
- "rtmps",
- "rtmpe",
- "rtmpte",
- "rtmpfp"
-};
-
-CRTMP::CRTMP() : m_socket(0)
-{
- for (int i=0; i<65600; i++)
- {
- m_vecChannelsIn[i] = NULL;
- m_vecChannelsOut[i] = NULL;
- }
- Close();
- m_pBuffer = new char[RTMP_BUFFER_CACHE_SIZE];
- m_nBufferMS = 300;
- m_fDuration = 0;
- m_stream_id = -1;
- m_pBufferStart = NULL;
- m_fAudioCodecs = 3191.0;
- m_fVideoCodecs = 252.0;
- m_bTimedout = false;
- m_bPausing = 0;
- m_mediaChannel = 0;
-}
-
-CRTMP::~CRTMP()
-{
- Close();
- delete [] m_pBuffer;
-}
-
-double CRTMP::GetDuration() { return m_fDuration; }
-bool CRTMP::IsConnected() { return m_socket != 0; }
-bool CRTMP::IsTimedout() { return m_bTimedout; }
-
-void CRTMP::SetBufferMS(int size)
-{
- m_nBufferMS = size;
-}
-
-void CRTMP::UpdateBufferMS()
-{
- SendCtrl(3, m_stream_id, m_nBufferMS);
-}
-
-void CRTMP::SetupStream(
- int protocol,
- const char *hostname,
- unsigned int port,
- const char *sockshost,
- const char *playpath,
- const char *tcUrl,
- const char *swfUrl,
- const char *pageUrl,
- const char *app,
- const char *auth,
- const char *swfSHA256Hash,
- uint32_t swfSize,
- const char *flashVer,
- const char *subscribepath,
- double dTime,
- uint32_t dLength,
- bool bLiveStream,
- long int timeout
-)
-{
- assert(protocol < 6);
-
- Log(LOGDEBUG, "Protocol : %s", RTMPProtocolStrings[protocol]);
- Log(LOGDEBUG, "Hostname : %s", hostname);
- Log(LOGDEBUG, "Port : %d", port);
- Log(LOGDEBUG, "Playpath : %s", playpath);
-
- if(tcUrl)
- Log(LOGDEBUG, "tcUrl : %s", tcUrl);
- if(swfUrl)
- Log(LOGDEBUG, "swfUrl : %s", swfUrl);
- if(pageUrl)
- Log(LOGDEBUG, "pageUrl : %s", pageUrl);
- if(app)
- Log(LOGDEBUG, "app : %s", app);
- if(auth)
- Log(LOGDEBUG, "auth : %s", auth);
- if(subscribepath)
- Log(LOGDEBUG, "subscribepath : %s", subscribepath);
- if(flashVer)
- Log(LOGDEBUG, "flashVer : %s", flashVer);
- if(dTime > 0)
- Log(LOGDEBUG, "SeekTime : %.3f sec", (double)dTime/1000.0);
- if(dLength > 0)
- Log(LOGDEBUG, "playLength : %.3f sec", (double)dLength/1000.0);
-
- Log(LOGDEBUG, "live : %s", bLiveStream ? "yes":"no");
- Log(LOGDEBUG, "timeout : %d sec", timeout);
-
- if(swfSHA256Hash != NULL && swfSize > 0) {
- Link.SWFHash = swfSHA256Hash;
- Link.SWFSize = swfSize;
- Log(LOGDEBUG, "SWFSHA256:");
- LogHex(LOGDEBUG, Link.SWFHash, 32);
- Log(LOGDEBUG, "SWFSize : %lu", Link.SWFSize);
- } else {
- Link.SWFHash = NULL;
- Link.SWFSize = 0;
- }
-
- if(sockshost)
- {
- const char *socksport = strchr(sockshost, ':');
- char *hostname = strdup(sockshost);
-
- if(socksport)
- hostname[socksport - sockshost] = '\0';
- Link.sockshost = hostname;
-
- Link.socksport = socksport ? atoi(socksport + 1) : 1080;
- Log(LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", Link.sockshost, Link.socksport);
- } else {
- Link.sockshost = NULL;
- Link.socksport = 0;
- }
-
-
- Link.tcUrl = tcUrl;
- Link.swfUrl = swfUrl;
- Link.pageUrl = pageUrl;
- Link.app = app;
- Link.auth = auth;
- Link.flashVer = flashVer;
- Link.subscribepath = subscribepath;
- Link.seekTime = dTime;
- Link.length = dLength;
- Link.bLiveStream = bLiveStream;
- Link.timeout = timeout;
-
- Link.protocol = protocol;
- Link.hostname = hostname;
- Link.port = port;
- Link.playpath = playpath;
-
- if (Link.port == 0)
- Link.port = 1935;
-}
-
-static bool add_addr_info(sockaddr_in* service, const char *hostname, int port)
-{
- service->sin_addr.s_addr = inet_addr(hostname);
- if (service->sin_addr.s_addr == INADDR_NONE)
- {
- struct hostent *host = gethostbyname(hostname);
- if (host == NULL || host->h_addr == NULL)
- {
- Log(LOGERROR, "Problem accessing the DNS. (addr: %s)", hostname);
- return false;
- }
- service->sin_addr = *(struct in_addr*)host->h_addr;
- }
-
- service->sin_port = htons(port);
- return true;
-}
-
-bool CRTMP::Connect() {
- if (!Link.hostname)
- return false;
-
- // close any previous connection
- Close();
-
- m_bTimedout = false;
- m_bPausing = 0;
- m_fDuration = 0.0;
-
- sockaddr_in service;
- memset(&service, 0, sizeof(sockaddr_in));
- service.sin_family = AF_INET;
-
- if (Link.socksport)
- {
- // Connect via SOCKS
- if(!add_addr_info(&service, Link.sockshost, Link.socksport)) return false;
- } else {
- // Connect directly
- if(!add_addr_info(&service, Link.hostname, Link.port)) return false;
- }
-
- m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (m_socket != -1)
- {
- if (connect(m_socket, (sockaddr*) &service, sizeof(struct sockaddr)) < 0)
- {
- int err = GetSockError();
- Log(LOGERROR, "%s, failed to connect socket. %d (%s)", __FUNCTION__,
- err, strerror(err));
- Close();
- return false;
- }
-
- if(Link.socksport) {
- Log(LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__);
- if (!SocksNegotiate())
- {
- Log(LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__);
- Close();
- return false;
- }
- }
-
- Log(LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__);
- if (!HandShake())
- {
- Log(LOGERROR, "%s, handshake failed.", __FUNCTION__);
- Close();
- return false;
- }
-
- Log(LOGDEBUG, "%s, handshaked", __FUNCTION__);
- if (!RTMPConnect())
- {
- Log(LOGERROR, "%s, RTMP connect failed.", __FUNCTION__);
- Close();
- return false;
- }
- // set timeout
- struct timeval tv;
- memset(&tv, 0, sizeof(tv));
- tv.tv_sec = Link.timeout;
- if (setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) {
- Log(LOGERROR,"%s, Setting socket timeout to %ds failed!", __FUNCTION__, tv.tv_sec);
- }
- }
- else
- {
- Log(LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__, GetSockError());
- return false;
- }
-
- int on = 1;
- setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
- return true;
-}
-
-bool CRTMP::SocksNegotiate() {
- sockaddr_in service;
- memset(&service, 0, sizeof(sockaddr_in));
-
- add_addr_info(&service, Link.hostname, Link.port);
- unsigned long addr = htonl(service.sin_addr.s_addr);
-
- char packet[] = {
- 4, 1, // SOCKS 4, connect
- (Link.port >> 8) & 0xFF,
- (Link.port) & 0xFF,
- (char) (addr >> 24) & 0xFF, (char) (addr >> 16) & 0xFF,
- (char) (addr >> 8) & 0xFF, (char) addr & 0xFF,
- 0}; // NULL terminate
-
- WriteN(packet, sizeof packet);
-
- if(ReadN(packet, 8) != 8)
- return false;
-
- if(packet[0] == 0 && packet[1] == 90) {
- return true;
- } else {
- Log(LOGERROR, "%s, SOCKS returned error code %d", packet[1]);
- return false;
- }
-}
-
-bool CRTMP::ConnectStream(double seekTime, uint32_t dLength) {
- if (seekTime >= -2.0)
- Link.seekTime = seekTime;
-
- if (dLength >= 0)
- Link.length = dLength;
-
- m_mediaChannel = 0;
- RTMPPacket packet;
- while (!m_bPlaying && IsConnected() && ReadPacket(packet)) {
- if (!packet.IsReady())
- {
- packet.FreePacket();
- continue;
- }
-
- if ((packet.m_packetType == 0x8) || \
- (packet.m_packetType == 0x9) || \
- (packet.m_packetType == 0x16))
- {
- Log(LOGDEBUG, "%s, received FLV packet before play()!", __FUNCTION__);
- break;
- }
-
- HandlePacket(packet);
- }
-
- return m_bPlaying;
-}
-
-bool CRTMP::ReconnectStream(int bufferTime, double seekTime, uint32_t dLength) {
- DeleteStream();
-
- SendCreateStream(2.0);
-
- SetBufferMS(bufferTime);
-
- return ConnectStream(seekTime, dLength);
-}
-
-bool CRTMP::ToggleStream()
-{
- bool res;
-
- res = SendPause(true, m_pauseStamp);
- if (!res) return res;
-
- m_bPausing = 1;
- sleep(1);
- res = SendPause(false, m_pauseStamp);
- m_bPausing = 3;
- return res;
-}
-
-void CRTMP::DeleteStream() {
- if (m_stream_id < 0)
- return;
-
- m_bPlaying = false;
-
- SendDeleteStream(m_stream_id);
-
- // No response expected for deleteStream
- if (m_methodCalls.back() == "deleteStream")
- m_methodCalls.erase(m_methodCalls.end());
-}
-
-int CRTMP::GetNextMediaPacket(RTMPPacket &packet)
-{
- int bHasMediaPacket = 0;
- while (!bHasMediaPacket && IsConnected() && ReadPacket(packet))
- {
- if (!packet.IsReady())
- {
- packet.FreePacket();
- //usleep(5000); // 5ms
- continue;
- }
-
- bHasMediaPacket = HandlePacket(packet);
-
- if (!bHasMediaPacket) {
- packet.FreePacket();
- } else if (m_bPausing == 3) {
- if (packet.m_nTimeStamp <= m_mediaStamp) {
- bHasMediaPacket = 0;
-#ifdef _DEBUG
- Log(LOGDEBUG, "Skipped type: %02X, size: %d, TS: %d ms, abs TS: %d, pause: %d ms", packet.m_packetType, packet.m_nBodySize, packet.m_nTimeStamp, packet.m_hasAbsTimestamp, m_mediaStamp);
-#endif
- continue;
- }
- m_bPausing = 0;
- }
- }
-
- if (bHasMediaPacket)
- m_bPlaying = true;
- else if (m_bTimedout)
- m_pauseStamp = m_channelTimestamp[m_mediaChannel];
-
- return bHasMediaPacket;
-}
-
-int CRTMP::HandlePacket(RTMPPacket &packet) {
- int bHasMediaPacket = 0;
- switch (packet.m_packetType)
- {
- case 0x01:
- // chunk size
- HandleChangeChunkSize(packet);
- break;
-
- case 0x03:
- // bytes read report
- Log(LOGDEBUG, "%s, received: bytes read report", __FUNCTION__);
- break;
-
- case 0x04:
- // ctrl
- HandleCtrl(packet);
- break;
-
- case 0x05:
- // server bw
- HandleServerBW(packet);
- break;
-
- case 0x06:
- // client bw
- HandleClientBW(packet);
- break;
-
- case 0x08:
- // audio data
- //Log(LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
- HandleAudio(packet);
- bHasMediaPacket = 1;
- if (!m_mediaChannel)
- m_mediaChannel = packet.m_nChannel;
- if (!m_bPausing)
- m_mediaStamp = packet.m_nTimeStamp;
- break;
-
- case 0x09:
- // video data
- //Log(LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
- HandleVideo(packet);
- bHasMediaPacket = 1;
- if (!m_mediaChannel)
- m_mediaChannel = packet.m_nChannel;
- if (!m_bPausing)
- m_mediaStamp = packet.m_nTimeStamp;
- break;
-
- case 0x0F: // flex stream send
- Log(LOGDEBUG, "%s, flex stream send, size %lu bytes, not supported, ignoring", __FUNCTION__, packet.m_nBodySize);
- break;
-
- case 0x10: // flex shared object
- Log(LOGDEBUG, "%s, flex shared object, size %lu bytes, not supported, ignoring", __FUNCTION__, packet.m_nBodySize);
- break;
-
- case 0x11: // flex message
- {
- Log(LOGDEBUG, "%s, flex message, size %lu bytes, not fully supported", __FUNCTION__, packet.m_nBodySize);
- //LogHex(packet.m_body, packet.m_nBodySize);
-
- // some DEBUG code
- /*RTMP_LIB::AMFObject obj;
- int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
- if(nRes < 0) {
- Log(LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
- //return;
- }
-
- obj.Dump();*/
-
- if ( HandleInvoke(packet.m_body+1, packet.m_nBodySize-1) == 1 )
- bHasMediaPacket = 2;
- break;
- }
- case 0x12:
- // metadata (notify)
- Log(LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__, packet.m_nBodySize);
- if ( HandleMetadata(packet.m_body, packet.m_nBodySize) )
- bHasMediaPacket = 1;
- break;
-
- case 0x13:
- Log(LOGDEBUG, "%s, shared object, not supported, ignoring", __FUNCTION__);
- break;
-
- case 0x14:
- // invoke
- Log(LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__, packet.m_nBodySize);
- //LogHex(packet.m_body, packet.m_nBodySize);
-
- if ( HandleInvoke(packet.m_body, packet.m_nBodySize) == 1 )
- bHasMediaPacket = 2;
- break;
-
- case 0x16:
- {
- // go through FLV packets and handle metadata packets
- unsigned int pos=0;
- uint32_t nTimeStamp = packet.m_nTimeStamp;
-
- while(pos+11 < packet.m_nBodySize) {
- uint32_t dataSize = CRTMP::ReadInt24(packet.m_body+pos+1); // size without header (11) and prevTagSize (4)
-
- if(pos+11+dataSize+4 > packet.m_nBodySize) {
- Log(LOGWARNING, "Stream corrupt?!");
- break;
- }
- if(packet.m_body[pos] == 0x12) {
- HandleMetadata(packet.m_body+pos+11, dataSize);
- } else if (packet.m_body[pos] == 8 || packet.m_body[pos] == 9) {
- nTimeStamp = CRTMP::ReadInt24(packet.m_body+pos+4);
- nTimeStamp |= (packet.m_body[pos+7]<<24);
- }
- pos += (11+dataSize+4);
- }
- if (!m_bPausing)
- m_mediaStamp = nTimeStamp;
-
- // FLV tag(s)
- //Log(LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize);
- bHasMediaPacket = 1;
- break;
- }
- default:
- Log(LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, packet.m_packetType);
- #ifdef _DEBUG
- LogHex(LOGDEBUG, packet.m_body, packet.m_nBodySize);
- #endif
- }
-
- return bHasMediaPacket;
-}
-
-#ifdef _DEBUG
-extern FILE *netstackdump;
-extern FILE *netstackdump_read;
-#endif
-
-int CRTMP::ReadN(char *buffer, int n)
-{
- int nOriginalSize = n;
-
- m_bTimedout = false;
-
- #ifdef _DEBUG
- memset(buffer, 0, n);
- #endif
-
- char *ptr = buffer;
- while (n > 0)
- {
- int nBytes = 0;
- if(m_nBufferSize == 0)
- if (!FillBuffer()) {
- if (!m_bTimedout)
- Close();
- return 0;
- }
- int nRead = ((n<m_nBufferSize)?n:m_nBufferSize);
- if(nRead > 0) {
- memcpy(ptr, m_pBufferStart, nRead);
- m_pBufferStart += nRead;
- m_nBufferSize -= nRead;
- nBytes = nRead;
- m_nBytesIn += nRead;
- if(m_nBytesIn > m_nBytesInSent + m_nClientBW/2 )
- SendBytesReceived();
- }
-
-//again:
-// nBytes = recv(m_socket, ptr, n, 0);
-
- //Log(LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes);
-#ifdef _DEBUG
- fwrite(ptr, 1, nBytes, netstackdump_read);
-#endif
-
- if (nBytes == 0)
- {
- Log(LOGDEBUG, "%s, RTMP socket closed by server", __FUNCTION__);
- //goto again;
- Close();
- break;
- }
-
- #ifdef CRYPTO
- if(Link.rc4keyIn) {
- RC4(Link.rc4keyIn, nBytes, (uint8_t*)ptr, (uint8_t*)ptr);
- }
- #endif
-
- n -= nBytes;
- ptr += nBytes;
- }
-
- return nOriginalSize - n;
-}
-
-bool CRTMP::WriteN(const char *buffer, int n)
-{
- const char *ptr = buffer;
-
- char *encrypted = 0;
-
- #ifdef CRYPTO
- if(Link.rc4keyOut) {
- ptr = encrypted = (char *)malloc(n);
- RC4(Link.rc4keyOut, n, (uint8_t*)buffer, (uint8_t*)ptr);
- }
- #endif
-
- while (n > 0)
- {
-#ifdef _DEBUG
- fwrite(ptr, 1, n, netstackdump);
-#endif
-
- int nBytes = send(m_socket, ptr, n, 0);
- //Log(LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes);
-
- if (nBytes < 0)
- {
- int sockerr = GetSockError();
- Log(LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__, sockerr, n);
-
- if (sockerr == EINTR && !bCtrlC)
- continue;
-
- Close();
-
- if(encrypted)
- free(encrypted);
- return false;
- }
-
- if (nBytes == 0)
- break;
-
- n -= nBytes;
- ptr += nBytes;
- }
-
- if(encrypted)
- free(encrypted);
-
- return n == 0;
-}
-
-bool CRTMP::RTMPConnect()
-{
- if (!SendConnectPacket())
- {
- Log(LOGERROR, "%s, failed to send connect RTMP packet", __FUNCTION__);
- return false;
- }
-
- return true;
-}
-
-bool CRTMP::SendConnectPacket()
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
- packet.m_packetType = 0x14; // INVOKE
- packet.AllocPacket(4096);
-
- char *enc = packet.m_body;
- enc += EncodeString(enc, "connect");
- enc += EncodeNumber(enc, 1.0);
- *enc = 0x03; //Object Datatype
- enc++;
-
- if(Link.app)
- enc += EncodeString(enc, "app", Link.app);
- if(Link.flashVer)
- enc += EncodeString(enc, "flashVer", Link.flashVer);
- if(Link.swfUrl)
- enc += EncodeString(enc, "swfUrl", Link.swfUrl);
- if(Link.tcUrl)
- enc += EncodeString(enc, "tcUrl", Link.tcUrl);
-
- enc += EncodeBoolean(enc, "fpad", false);
- enc += EncodeNumber(enc, "capabilities", 15.0);
- enc += EncodeNumber(enc, "audioCodecs", m_fAudioCodecs);
- enc += EncodeNumber(enc, "videoCodecs", m_fVideoCodecs);
- enc += EncodeNumber(enc, "videoFunction", 1.0);
- if(Link.pageUrl)
- enc += EncodeString(enc, "pageUrl", Link.pageUrl);
-
- enc += EncodeNumber(enc, "objectEncoding", 0.0); // AMF0, AMF3 not supported yet
- enc += 2; // end of object - 0x00 0x00 0x09
- *enc = 0x09;
- enc++;
-
- //enc += EncodeString(enc, "user"); // DEBUG, REMOVE!!!
- //*enc = 0x05; enc++;
- //enc += EncodeString(enc, "tvmanele1"); // DEBUG, REMOVE!!
-
- // add auth string
- if(Link.auth)
- {
- *enc = 0x01; enc++;
- *enc = 0x01; enc++;
-
- enc += EncodeString(enc, Link.auth);
- }
- packet.m_nBodySize = enc-packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendBGHasStream(double dId, char *playpath)
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // INVOKE
-
- packet.AllocPacket(1024); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "bgHasStream");
- enc += EncodeNumber(enc, dId);
- *enc = 0x05; // NULL
- enc++;
-
- enc += EncodeString(enc, playpath);
-
- packet.m_nBodySize = enc-packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendCreateStream(double dStreamId)
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // INVOKE
-
- packet.AllocPacket(256); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "createStream");
- enc += EncodeNumber(enc, dStreamId);
- *enc = 0x05; // NULL
- enc++;
-
- packet.m_nBodySize = enc - packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendFCSubscribe(const char *subscribepath)
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // INVOKE
-
- packet.AllocPacket(256); // should be enough
- Log(LOGDEBUG, "FCSubscribe: %s", subscribepath);
- char *enc = packet.m_body;
- enc += EncodeString(enc, "FCSubscribe");
- enc += EncodeNumber(enc, 4.0);
- *enc = 0x05; // NULL
- enc++;
- enc += EncodeString(enc, subscribepath);
-
- packet.m_nBodySize = enc - packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendDeleteStream(double dStreamId)
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // INVOKE
-
- packet.AllocPacket(256); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "deleteStream");
- enc += EncodeNumber(enc, 0.0);
- *enc = 0x05; // NULL
- enc++;
- enc += EncodeNumber(enc, dStreamId);
-
- packet.m_nBodySize = enc - packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendPause(bool DoPause, double dTime)
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x08; // video channel
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // invoke
-
- packet.AllocPacket(256); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "pause");
- enc += EncodeNumber(enc, 0);
- *enc = 0x05; // NULL
- enc++;
- enc += EncodeBoolean(enc, DoPause);
- enc += EncodeNumber(enc, (double)dTime);
-
- packet.m_nBodySize = enc - packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendSeek(double dTime)
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x08; // video channel
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // invoke
-
- packet.AllocPacket(256); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "seek");
- enc += EncodeNumber(enc, 0);
- *enc = 0x05; // NULL
- enc++;
- enc += EncodeNumber(enc, dTime);
-
- packet.m_nBodySize = enc - packet.m_body;
-
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendServerBW()
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x02; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
- packet.m_packetType = 0x05; // Server BW
-
- packet.AllocPacket(4);
- packet.m_nBodySize = 4;
-
- EncodeInt32(packet.m_body, m_nServerBW);
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendBytesReceived()
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x02; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x03; // bytes in
-
- packet.AllocPacket(4);
- packet.m_nBodySize = 4;
-
- EncodeInt32(packet.m_body, m_nBytesIn); // hard coded for now
- m_nBytesInSent = m_nBytesIn;
-
- //Log(LOGDEBUG, "Send bytes report. 0x%x (%d bytes)", (unsigned int)m_nBytesIn, m_nBytesIn);
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendCheckBW()
-{
- RTMPPacket packet;
-
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
- packet.m_packetType = 0x14; // INVOKE
- packet.m_nInfoField1 = GetTime();
-
- packet.AllocPacket(256); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "_checkbw");
- enc += EncodeNumber(enc, 0);
- *enc = 0x05; // NULL
- enc++;
-
- packet.m_nBodySize = enc - packet.m_body;
-
- // triggers _onbwcheck and eventually results in _onbwdone
- return SendRTMP(packet);
-}
-
-bool CRTMP::SendCheckBWResult(double txn)
-{
- RTMPPacket packet;
- bool res;
-
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14; // INVOKE
- packet.m_nInfoField1 = 0x16 * m_nBWCheckCounter; // temp inc value. till we figure it out.
-
- packet.AllocPacket(256); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "_result");
- enc += EncodeNumber(enc, txn);
- *enc = 0x05; // NULL
- enc++;
- enc += EncodeNumber(enc, (double)m_nBWCheckCounter++);
-
- packet.m_nBodySize = enc - packet.m_body;
-
- res = SendRTMP(packet);
- m_methodCalls.erase(m_methodCalls.end());
- return res;
-}
-
-bool CRTMP::SendPlay()
-{
- RTMPPacket packet;
- packet.m_nChannel = 0x08; // we make 8 our stream channel
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
- packet.m_packetType = 0x14; // INVOKE
- packet.m_nInfoField2 = m_stream_id; //0x01000000;
-
- packet.AllocPacket(1024); // should be enough
- char *enc = packet.m_body;
- enc += EncodeString(enc, "play");
- enc += EncodeNumber(enc, 0.0); // stream id??
- *enc = 0x05; // NULL
- enc++;
-
- Log(LOGDEBUG, "%s, seekTime=%.2f, dLength=%d, sending play: %s", __FUNCTION__, Link.seekTime, Link.length, Link.playpath);
- enc += EncodeString(enc, Link.playpath);
-
- // Optional parameters start and len.
-
- // start: -2, -1, 0, positive number
- // -2: looks for a live stream, then a recorded stream, if not found any open a live stream
- // -1: plays a live stream
- // >=0: plays a recorded streams from 'start' milliseconds
- if(Link.bLiveStream)
- enc += EncodeNumber(enc, -1000.0);
- else {
- if(Link.seekTime > 0.0)
- enc += EncodeNumber(enc, Link.seekTime); // resume from here
- else
- enc += EncodeNumber(enc, 0.0);//-2000.0); // recorded as default, -2000.0 is not reliable since that freezes the player if the stream is not found
- }
-
- // len: -1, 0, positive number
- // -1: plays live or recorded stream to the end (default)
- // 0: plays a frame 'start' ms away from the beginning
- // >0: plays a live or recoded stream for 'len' milliseconds
- //enc += EncodeNumber(enc, -1.0); // len
- if(Link.length)
- enc += EncodeNumber(enc, Link.length); // len
-
- packet.m_nBodySize = enc - packet.m_body;
-
- return SendRTMP(packet);
-}
-/*
-from http://jira.red5.org/confluence/display/docs/Ping:
-
-Ping is the most mysterious message in RTMP and till now we haven't fully interpreted it yet. In summary, Ping message is used as a special command that are exchanged between client and server. This page aims to document all known Ping messages. Expect the list to grow.
-
-The type of Ping packet is 0x4 and contains two mandatory parameters and two optional parameters. The first parameter is the type of Ping and in short integer. The second parameter is the target of the ping. As Ping is always sent in Channel 2 (control channel) and the target object in RTMP header is always 0 which means the Connection object, it's necessary to put an extra parameter to indicate the exact target object the Ping is sent to. The second parameter takes this responsibility. The value has the same meaning as the target object field in RTMP header. (The second value could also be used as other purposes, like RTT Ping/Pong. It is used as the timestamp.) The third and fourth parameters are optional and could be looked upon as the parameter of the Ping packet. Below is an unexhausted list of Ping messages.
-
- * type 0: Clear the stream. No third and fourth parameters. The second parameter could be 0. After the connection is established, a Ping 0,0 will be sent from server to client. The message will also be sent to client on the start of Play and in response of a Seek or Pause/Resume request. This Ping tells client to re-calibrate the clock with the timestamp of the next packet server sends.
- * type 1: Tell the stream to clear the playing buffer.
- * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
- * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
- * type 6: Ping the client from server. The second parameter is the current time.
- * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
- * type 26: SWFVerification request
- * type 27: SWFVerification response
-*/
-bool CRTMP::SendCtrl(short nType, unsigned int nObject, unsigned int nTime)
-{
- Log(LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType);
-
- RTMPPacket packet;
- packet.m_nChannel = 0x02; // control channel (ping)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x04; // ctrl
- packet.m_nInfoField1 = GetTime();
-
- int nSize = (nType==0x03?10:6); // type 3 is the buffer time and requires all 3 parameters. all in all 10 bytes.
- if(nType == 0x1B)
- nSize = 44;
-
- packet.AllocPacket(nSize);
- packet.m_nBodySize = nSize;
-
- char *buf = packet.m_body;
- buf += EncodeInt16(buf, nType);
-
- if(nType == 0x1B) {
- #ifdef CRYPTO
- memcpy(buf, Link.SWFVerificationResponse, 42);
- Log(LOGDEBUG, "Sending SWFVerification response: ");
- LogHex(LOGDEBUG, packet.m_body, packet.m_nBodySize);
- #endif
- } else {
- if (nSize > 2)
- buf += EncodeInt32(buf, nObject);
-
- if (nSize > 6)
- buf += EncodeInt32(buf, nTime);
- }
-
- return SendRTMP(packet);
-}
-
-// Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
-int CRTMP::HandleInvoke(const char *body, unsigned int nBodySize)
-{
- if (body[0] != 0x02) // make sure it is a string method name we start with
- {
- Log(LOGWARNING, "%s, Sanity failed. no string method in invoke packet", __FUNCTION__);
- return 0;
- }
-
- RTMP_LIB::AMFObject obj;
- int nRes = obj.Decode(body, nBodySize);
- if (nRes < 0)
- {
- Log(LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
- return 0;
- }
-
- obj.Dump();
- std::string method = obj.GetProperty(0).GetString();
- double txn = obj.GetProperty(1).GetNumber();
- Log(LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.c_str());
-
-#define CSCMP(a,b) (a.size() == (sizeof(b)-1)) && !strcmp(a.c_str(),b)
-
- if (CSCMP(method, "_result"))
- {
- std::string methodInvoked = m_methodCalls[0];
- m_methodCalls.erase(m_methodCalls.begin());
-
- Log(LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, methodInvoked.c_str());
-
- if (CSCMP(methodInvoked,"connect"))
- {
- SendServerBW();
- SendCtrl(3, 0, 300);
-
- SendCreateStream(2.0);
-
- // Send the FCSubscribe if live stream or if subscribepath is set
- if (Link.subscribepath)
- SendFCSubscribe(Link.subscribepath);
- else if (Link.bLiveStream)
- SendFCSubscribe(Link.playpath);
- }
- else if (CSCMP(methodInvoked,"createStream"))
- {
- m_stream_id = (int)obj.GetProperty(3).GetNumber();
-
- SendPlay();
- /* not required since we send the seek parameter in the play packet now
- if(Link.seekTime > 0) {
- Log(LOGDEBUG, "%s, sending seek: %f ms", __FUNCTION__, Link.seekTime);
- SendSeek(Link.seekTime);
- }*/
-
- SendCtrl(3, m_stream_id, m_nBufferMS);
- }
- else if (CSCMP(methodInvoked,"play"))
- {
- m_bPlaying = true;
- SendPlay();
- }
- }
- else if (CSCMP(method,"onBWDone"))
- {
- if(nBodySize < 25)
- SendCheckBW();
- else
- {
- /* Dunno why we get this bogus request */
- ;
- }
- }
- else if (CSCMP(method,"onFCSubscribe"))
- {
- // SendOnFCSubscribe();
- }
- else if (CSCMP(method,"onFCUnsubscribe"))
- {
- Close();
- return 1;
- }
- else if (CSCMP(method,"_onbwcheck"))
- {
- SendCheckBWResult(txn);
- }
- else if (CSCMP(method,"_onbwdone"))
- {
- std::vector<std::string>::iterator i; //remote method calls queue
- for (i=m_methodCalls.begin(); i<m_methodCalls.end(); i++)
- if (i[0] == "_checkbw") {
- m_methodCalls.erase(i);
- break;
- }
- }
- else if (CSCMP(method,"_error"))
- {
- Log(LOGERROR, "rtmp server sent error");
- }
- else if (CSCMP(method,"close"))
- {
- Log(LOGERROR, "rtmp server requested close");
- Close();
- }
- else if (CSCMP(method,"onStatus"))
- {
- std::string code = obj.GetProperty(3).GetObject().GetProperty("code").GetString();
- std::string level = obj.GetProperty(3).GetObject().GetProperty("level").GetString();
-
- Log(LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.c_str() );
- if (code == "NetStream.Failed"
- || code == "NetStream.Play.Failed"
- || code == "NetStream.Play.StreamNotFound"
- || code == "NetConnection.Connect.InvalidApp") {
- m_stream_id = -1;
- Close();
- }
-
- if (code == "NetStream.Play.Start") {
- std::vector<std::string>::iterator i; //remote method calls queue
- m_bPlaying = true;
- for (i=m_methodCalls.begin(); i<m_methodCalls.end(); i++)
- if (i[0] == "play") {
- m_methodCalls.erase(i);
- break;
- }
- }
-
- // Return 1 if this is a Play.Complete or Play.Stop
- if (code == "NetStream.Play.Complete"
- || code == "NetStream.Play.Stop") {
- Close();
- return 1;
- }
-
- /*if(Link.seekTime > 0) {
- if(code == "NetStream.Seek.Notify") { // seeked successfully, can play now!
- bSeekedSuccessfully = true;
- } else if(code == "NetStream.Play.Start" && !bSeekedSuccessfully) { // well, try to seek again
- Log(LOGWARNING, "%s, server ignored seek!", __FUNCTION__);
- }
- }*/
- }
- else
- {
-
- }
- return 0;
-}
-
-//int pnum=0;
-
-bool CRTMP::FindFirstMatchingProperty(AMFObject &obj, std::string name, AMFObjectProperty &p)
-{
- // this is a small object search to locate the "duration" property
- for (int n=0; n<obj.GetPropertyCount(); n++) {
- AMFObjectProperty prop = obj.GetProperty(n);
-
- if(prop.GetPropName() == name) {
-
- p = obj.GetProperty(n);
- return true;
- }
-
- if(prop.GetType() == AMF_OBJECT) {
- AMFObject next = prop.GetObject();
- return FindFirstMatchingProperty(next, name, p);
- }
- }
- return false;
-}
-
-bool CRTMP::DumpMetaData(AMFObject &obj)
-{
- std::string name;
- AMFObjectProperty prop;
- for (int n=0; n<obj.GetPropertyCount(); n++) {
- AMFObjectProperty prop = obj.GetProperty(n);
- if ( prop.GetType() != AMF_OBJECT ) {
- char str[256]="";
- switch( prop.GetType() )
- {
- case AMF_NUMBER:
- if ( (double)int(prop.GetNumber()) == prop.GetNumber() )
- snprintf(str, 255, "%.0f", prop.GetNumber() );
- else
- snprintf(str, 255, "%.2f", prop.GetNumber() );
- break;
- case AMF_BOOLEAN:
- snprintf(str, 255, "%s", prop.GetNumber() == 1.?"TRUE":"FALSE");
- break;
- case AMF_STRING:
- snprintf(str, 255, "%s", prop.GetString().c_str());
- break;
- case AMF_DATE:
- snprintf(str, 255, "timestamp:%.2f", prop.GetNumber() );
- break;
- default:
- snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop.GetType() );
- }
- if ( prop.GetPropName() != "" ) {
- // chomp
- if ( strlen(str) >= 1 && str[strlen(str)-1 ] == '\n')
- str[strlen(str)-1] = '\0';
- LogPrintf(" %-22s%s\n", prop.GetPropName().c_str(), str );
- }
- } else {
- if ( prop.GetPropName() != "" )
- LogPrintf("%s:\n", prop.GetPropName().c_str() );
- AMFObject next = prop.GetObject();
- DumpMetaData(next);
- }
- }
- return false;
-}
-
-bool CRTMP::HandleMetadata(char *body, unsigned int len)
-{
- /*Log(LOGDEBUG,"Parsing meta data: %d @0x%08X", packet.m_nBodySize, packet.m_body);
- LogHex(packet.m_body, packet.m_nBodySize);
-
- char str[256]={0};
- sprintf(str, "packet%d", pnum);
- pnum++;
- FILE *f = fopen(str, "wb");
- fwrite(packet.m_body, 1, packet.m_nBodySize, f);
- fclose(f);//*/
-
- // allright we get some info here, so parse it and print it
- // also keep duration or filesize to make a nice progress bar
-
- //int len = packet.m_nBodySize;
- //char *p = packet.m_body;
-
- RTMP_LIB::AMFObject obj;
- int nRes = obj.Decode(body, len);
- if(nRes < 0) {
- Log(LOGERROR, "%s, error decoding meta data packet", __FUNCTION__);
- return false;
- }
-
- obj.Dump();
- std::string metastring = obj.GetProperty(0).GetString();
-
- if(metastring == "onMetaData") {
- AMFObjectProperty prop;
- // Show metadata
- LogPrintf("\r%s\n", "Metadata: " );
- DumpMetaData(obj);
- if(FindFirstMatchingProperty(obj, "duration", prop)) {
- m_fDuration = prop.GetNumber();
- //Log(LOGDEBUG, "Set duration: %.2f", m_fDuration);
- }
- return true;
- }
- else
- {
- return false;
- }
-}
-
-void CRTMP::HandleChangeChunkSize(const RTMPPacket &packet)
-{
- if (packet.m_nBodySize >= 4)
- {
- m_chunkSize = ReadInt32(packet.m_body);
- Log(LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, m_chunkSize);
- }
-}
-
-void CRTMP::HandleAudio(const RTMPPacket &packet)
-{
-}
-
-void CRTMP::HandleVideo(const RTMPPacket &packet)
-{
-}
-
-void CRTMP::HandleCtrl(const RTMPPacket &packet)
-{
- short nType = -1;
- unsigned int tmp;
- if (packet.m_body && packet.m_nBodySize >= 2)
- nType = ReadInt16(packet.m_body);
- Log(LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType, packet.m_nBodySize);
- //LogHex(packet.m_body, packet.m_nBodySize);
-
- if (packet.m_nBodySize >= 6) {
- switch(nType) {
- case 0:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream Begin %d", __FUNCTION__, tmp);
- break;
-
- case 1:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream EOF %d", __FUNCTION__, tmp);
- if (m_bPausing == 1)
- m_bPausing = 2;
- break;
-
- case 2:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream Dry %d", __FUNCTION__, tmp);
- break;
-
- case 4:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream IsRecorded %d", __FUNCTION__, tmp);
- break;
-
- case 6: // server ping. reply with pong.
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Ping %d", __FUNCTION__, tmp);
- SendCtrl(0x07, tmp);
- break;
-
- case 31:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream BufferEmpty %d", __FUNCTION__, tmp);
- if (!m_bPausing) {
- m_pauseStamp = m_channelTimestamp[m_mediaChannel];
- SendPause(true, m_pauseStamp);
- m_bPausing = 1;
- } else if (m_bPausing == 2) {
- SendPause(false, m_pauseStamp);
- m_bPausing = 3;
- }
- break;
-
- case 32:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream BufferReady %d", __FUNCTION__, tmp);
- break;
-
- default:
- tmp = ReadInt32(packet.m_body + 2);
- Log(LOGDEBUG, "%s, Stream xx %d", __FUNCTION__, tmp);
- break;
- }
-
- }
-
- if (nType == 0x1A) {
- Log(LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__);
- //LogHex(packet.m_body, packet.m_nBodySize);
-
- // respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied
- if(Link.SWFHash) {
- SendCtrl(0x1B, 0, 0);
- } else {
- Log(LOGWARNING, "%s: Ignoring SWFVerification request, use --swfhash and --swfsize!", __FUNCTION__);
- }
- }
-}
-
-void CRTMP::HandleServerBW(const RTMPPacket &packet) {
- m_nServerBW = ReadInt32(packet.m_body);
- Log(LOGDEBUG, "%s: server BW = %d", __FUNCTION__, m_nServerBW);
-}
-
-void CRTMP::HandleClientBW(const RTMPPacket &packet) {
- m_nClientBW = ReadInt32(packet.m_body);
- if (packet.m_nBodySize > 4)
- m_nClientBW2 = packet.m_body[4];
- else
- m_nClientBW2 = -1;
- Log(LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, m_nClientBW, m_nClientBW2);
-}
-
-bool CRTMP::ReadPacket(RTMPPacket &packet)
-{
- char type;
- if (ReadN(&type,1) == 0)
- {
- Log(LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__);
- return false;
- }
-
- packet.m_headerType = (type & 0xc0) >> 6;
- packet.m_nChannel = (type & 0x3f);
- if ( packet.m_nChannel == 0 )
- {
- if (ReadN(&type,1) != 1)
- {
- Log(LOGERROR, "%s, failed to read RTMP packet header 2nd byte", __FUNCTION__);
- return false;
- }
- packet.m_nChannel = (unsigned)type;
- packet.m_nChannel += 64;
- } else if ( packet.m_nChannel == 1 )
- {
- char t[2];
- int tmp;
- if (ReadN(t,2) != 2)
- {
- Log(LOGERROR, "%s, failed to read RTMP packet header 3nd byte", __FUNCTION__);
- return false;
- }
- tmp = (((unsigned)t[1])<<8) + (unsigned)t[0];
- packet.m_nChannel = tmp + 64;
- Log(LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet.m_nChannel);
- }
-
- int nSize = packetSize[packet.m_headerType];
-
- if (nSize == RTMP_LARGE_HEADER_SIZE) // if we get a full header the timestamp is absolute
- packet.m_hasAbsTimestamp = true;
-
- if (nSize < RTMP_LARGE_HEADER_SIZE) { // using values from the last message of this channel
- packet.FreePacketHeader(); // test whether this avoids memory leak
- if (m_vecChannelsIn[packet.m_nChannel])
- packet = *m_vecChannelsIn[packet.m_nChannel];
- }
-
- nSize--;
-
- char header[RTMP_LARGE_HEADER_SIZE] = {0};
- if (nSize > 0 && ReadN(header,nSize) != nSize)
- {
- Log(LOGERROR, "%s, failed to read RTMP packet header. type: %x", __FUNCTION__, (unsigned int)type);
- return false;
- }
-
- if (nSize >= 3)
- packet.m_nInfoField1 = ReadInt24(header);
-
- //Log(LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nInfoField1, packet.m_hasAbsTimestamp);
-
- if (nSize >= 6)
- {
- packet.m_nBodySize = ReadInt24(header + 3);
- packet.m_nBytesRead = 0;
- packet.FreePacketHeader(); // new packet body
- }
-
- if (nSize > 6)
- packet.m_packetType = header[6];
-
- if (nSize == 11)
- packet.m_nInfoField2 = ReadInt32LE(header+7);
-
- bool didAlloc = false;
- if (packet.m_nBodySize > 0 && packet.m_body == NULL)
- {
- if (!packet.AllocPacket(packet.m_nBodySize)) {
- Log(LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__);
- return false;
- }
- didAlloc = true;
- }
-
- int nToRead = packet.m_nBodySize - packet.m_nBytesRead;
- int nChunk = m_chunkSize;
- if (nToRead < nChunk)
- nChunk = nToRead;
-
- if (ReadN(packet.m_body + packet.m_nBytesRead, nChunk) != nChunk)
- {
- Log(LOGERROR, "%s, failed to read RTMP packet body. len: %lu", __FUNCTION__, packet.m_nBodySize);
- if (!didAlloc) {
- packet.m_body = NULL; // we dont want it deleted since its pointed to from the stored packets (m_vecChannelsIn)
- packet.m_buffer = NULL;
- }
- return false;
- }
-
- packet.m_nBytesRead += nChunk;
-
- // keep the packet as ref for other packets on this channel
- if (!m_vecChannelsIn[packet.m_nChannel])
- m_vecChannelsIn[packet.m_nChannel] = new RTMPPacket;
- *m_vecChannelsIn[packet.m_nChannel] = packet;
-
- if (packet.IsReady())
- {
- packet.m_nTimeStamp = packet.m_nInfoField1;
-
- // make packet's timestamp absolute
- if (!packet.m_hasAbsTimestamp)
- packet.m_nTimeStamp += m_channelTimestamp[packet.m_nChannel]; // timestamps seem to be always relative!!
-
- m_channelTimestamp[packet.m_nChannel] = packet.m_nTimeStamp;
-
- // reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel
- // arrives and requests to re-use some info (small packet header)
- m_vecChannelsIn[packet.m_nChannel]->m_body = NULL;
- m_vecChannelsIn[packet.m_nChannel]->m_buffer = NULL;
- m_vecChannelsIn[packet.m_nChannel]->m_nBytesRead = 0;
- m_vecChannelsIn[packet.m_nChannel]->m_hasAbsTimestamp = false; // can only be false if we reuse header
- }
- else {
- packet.m_body = NULL; // so it wont be erased on "free"
- packet.m_buffer = NULL; // so it wont be erased on "free"
- }
-
- return true;
-}
-
-unsigned short CRTMP::ReadInt16(const char *data)
-{
- unsigned char *c = (unsigned char *)data;
- unsigned short val;
- val = (c[0] << 8) | c[1];
- return val;
-}
-
-unsigned int CRTMP::ReadInt24(const char *data)
-{
- unsigned char *c = (unsigned char *)data;
- unsigned int val;
- val = (c[0] << 16) | (c[1] << 8) | c[2];
- return val;
-}
-
-// big-endian 32bit integer
-unsigned int CRTMP::ReadInt32(const char *data)
-{
- unsigned char *c = (unsigned char *)data;
- unsigned int val;
- val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
- return val;
-}
-
-std::string CRTMP::ReadString(const char *data)
-{
- std::string strRes;
- short len = ReadInt16(data);
- if (len > 0)
- {
- char *pStr = new char[len+1];
- memset(pStr, 0, len+1);
- memcpy(pStr, data + sizeof(short), len);
- strRes = pStr;
- delete [] pStr;
- }
- return strRes;
-}
-
-bool CRTMP::ReadBool(const char *data)
-{
- return *data == 0x01;
-}
-
-int CRTMP::EncodeString(char *output, const std::string &strName, const std::string &strValue)
-{
- char *buf = output;
- short length = htons(strName.size());
- memcpy(buf, &length, 2);
- buf += 2;
-
- memcpy(buf, strName.c_str(), strName.size());
- buf += strName.size();
-
- buf += EncodeString(buf, strValue);
- return buf - output;
-}
-
-int CRTMP::EncodeInt16(char *output, short nVal)
-{
- output[1] = nVal & 0xff;
- output[0] = nVal >> 8;
- return sizeof(short);
-}
-
-int CRTMP::EncodeInt24(char *output, int nVal)
-{
- output[2] = nVal & 0xff;
- output[1] = nVal >> 8;
- output[0] = nVal >> 16;
- return 3;
-}
-
-// big-endian 32bit integer
-int CRTMP::EncodeInt32(char *output, int nVal)
-{
- output[3] = nVal & 0xff;
- output[2] = nVal >> 8;
- output[1] = nVal >> 16;
- output[0] = nVal >> 24;
- return sizeof(int);
-}
-
-int CRTMP::EncodeNumber(char *output, const std::string &strName, double dVal)
-{
- char *buf = output;
-
- unsigned short length = htons(strName.size());
- memcpy(buf, &length, 2);
- buf += 2;
-
- memcpy(buf, strName.c_str(), strName.size());
- buf += strName.size();
-
- buf += EncodeNumber(buf, dVal);
- return buf - output;
-}
-
-int CRTMP::EncodeBoolean(char *output, const std::string &strName, bool bVal)
-{
- char *buf = output;
- unsigned short length = htons(strName.size());
- memcpy(buf, &length, 2);
- buf += 2;
-
- memcpy(buf, strName.c_str(), strName.size());
- buf += strName.size();
-
- buf += EncodeBoolean(buf, bVal);
-
- return buf - output;
-}
-
-int CRTMP::EncodeString(char *output, const std::string &strValue)
-{
- char *buf = output;
- *buf = 0x02; // Datatype: String
- buf++;
-
- short length = htons(strValue.size());
- memcpy(buf, &length, 2);
- buf += 2;
-
- memcpy(buf, strValue.c_str(), strValue.size());
- buf += strValue.size();
-
- return buf - output;
-}
-
-int CRTMP::EncodeNumber(char *output, double dVal)
-{
- char *buf = output;
- *buf = 0x00; // type: Number
- buf++;
-
- WriteNumber(buf, dVal);
- buf += 8;
-
- return buf - output;
-}
-
-int CRTMP::EncodeBoolean(char *output, bool bVal)
-{
- char *buf = output;
-
- *buf = 0x01; // type: Boolean
- buf++;
-
- *buf = bVal?0x01:0x00;
- buf++;
-
- return buf - output;
-}
-
-#ifndef CRYPTO
-bool CRTMP::HandShake(bool FP9HandShake)
-{
- char clientsig[RTMP_SIG_SIZE+1];
- char serversig[RTMP_SIG_SIZE];
-
- clientsig[0] = 0x03; // not encrypted
-
- uint32_t uptime = htonl(GetTime());
- memcpy(clientsig + 1, &uptime, 4);
-
- memset(&clientsig[5], 0, 4);
-
-#ifdef _DEBUG
- for (int i=9; i<RTMP_SIG_SIZE; i++)
- clientsig[i] = 0xff;
-#else
- for (int i=9; i<RTMP_SIG_SIZE; i++)
- clientsig[i] = (char)(rand() % 256);
-#endif
-
- if (!WriteN(clientsig, RTMP_SIG_SIZE + 1))
- return false;
-
- char type;
- if (ReadN(&type, 1) != 1) // 0x03 or 0x06
- return false;
-
- Log(LOGDEBUG, "%s: Type Answer : %02X", __FUNCTION__, type);
-
- if(type != clientsig[0])
- Log(LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d", __FUNCTION__, clientsig[0], type);
-
- if (ReadN(serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
- return false;
-
- // decode server response
- uint32_t suptime;
-
- memcpy(&suptime, serversig, 4);
- suptime = ntohl(suptime);
-
- Log(LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime);
- Log(LOGDEBUG, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__, serversig[4], serversig[5], serversig[6], serversig[7]);
-
- // 2nd part of handshake
- char resp[RTMP_SIG_SIZE];
- if (ReadN(resp, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
- return false;
-
- bool bMatch = (memcmp(resp, clientsig + 1, RTMP_SIG_SIZE) == 0);
- if (!bMatch)
- {
- Log(LOGWARNING, "%s, client signiture does not match!",__FUNCTION__);
- }
-
- if (!WriteN(serversig, RTMP_SIG_SIZE))
- return false;
-
- return true;
-}
-#endif
-
-bool CRTMP::SendRTMP(RTMPPacket &packet)
-{
- const RTMPPacket *prevPacket = m_vecChannelsOut[packet.m_nChannel];
- if (prevPacket && packet.m_headerType != RTMP_PACKET_SIZE_LARGE)
- {
- // compress a bit by using the prev packet's attributes
- if (prevPacket->m_nBodySize == packet.m_nBodySize && packet.m_headerType == RTMP_PACKET_SIZE_MEDIUM)
- packet.m_headerType = RTMP_PACKET_SIZE_SMALL;
-
- if (prevPacket->m_nInfoField2 == packet.m_nInfoField2 && packet.m_headerType == RTMP_PACKET_SIZE_SMALL)
- packet.m_headerType = RTMP_PACKET_SIZE_MINIMUM;
-
- }
-
- if (packet.m_headerType > 3) // sanity
- {
- Log(LOGERROR, "sanity failed!! tring to send header of type: 0x%02x.", (unsigned char)packet.m_headerType);
- return false;
- }
-
- int nSize = packetSize[packet.m_headerType];
- int hSize = nSize;
- char *header = packet.m_body - nSize;
- header[0] = (char)((packet.m_headerType << 6) | packet.m_nChannel);
- if (nSize > 1)
- EncodeInt24(header+1, packet.m_nInfoField1);
-
- if (nSize > 4)
- {
- EncodeInt24(header+4, packet.m_nBodySize);
- header[7] = packet.m_packetType;
- }
-
- if (nSize > 8)
- EncodeInt32LE(header+8, packet.m_nInfoField2);
-
- nSize = packet.m_nBodySize;
- char *buffer = packet.m_body;
- int nChunkSize = RTMP_DEFAULT_CHUNKSIZE;
-
- while (nSize)
- {
- int wrote;
-
- if (nSize < nChunkSize)
- nChunkSize = nSize;
-
- if (header) {
- wrote=WriteN(header, nChunkSize+hSize);
- header = NULL;
- } else {
- wrote=WriteN(buffer, nChunkSize);
- }
- if (!wrote)
- return false;
-
- nSize -= nChunkSize;
- buffer += nChunkSize;
-
- if (nSize > 0)
- {
- header = buffer-1;
- hSize = 1;
- *header = (0xc0 | packet.m_nChannel);
- }
- }
-
- if (packet.m_packetType == 0x14) { // we invoked a remote method, keep it in call queue till result arrives
- m_methodCalls.push_back(ReadString(packet.m_body + 1));
- Log(LOGDEBUG, "Invoking %s", ReadString(packet.m_body + 1).c_str());
- }
-
- if (!m_vecChannelsOut[packet.m_nChannel])
- m_vecChannelsOut[packet.m_nChannel] = new RTMPPacket;
- *m_vecChannelsOut[packet.m_nChannel] = packet;
- m_vecChannelsOut[packet.m_nChannel]->m_body = NULL;
- m_vecChannelsOut[packet.m_nChannel]->m_buffer = NULL;
- return true;
-}
-
-void CRTMP::Close()
-{
- if (IsConnected())
- close(m_socket);
-
- m_stream_id = -1;
- m_socket = 0;
- m_chunkSize = RTMP_DEFAULT_CHUNKSIZE;
- m_nBWCheckCounter = 0;
- m_nBytesIn = 0;
- m_nBytesInSent = 0;
- m_nClientBW = 2500000;
- m_nClientBW2 = 2;
- m_nServerBW = 2500000;
-
- for (int i=0; i<65600; i++)
- {
- if (m_vecChannelsIn[i]) {
- delete m_vecChannelsIn[i];
- m_vecChannelsIn[i] = NULL;
- }
- if (m_vecChannelsOut[i]) {
- delete m_vecChannelsOut[i];
- m_vecChannelsOut[i] = NULL;
- }
- }
- m_methodCalls.clear();
-
- m_bPlaying = false;
- m_nBufferSize = 0;
-}
-
-bool CRTMP::FillBuffer()
-{
- assert(m_nBufferSize == 0); // only fill buffer when it's empty
- int nBytes;
-
-again:
- nBytes = recv(m_socket, m_pBuffer, RTMP_BUFFER_CACHE_SIZE, 0);
- if(nBytes != -1) {
- m_nBufferSize += nBytes;
- m_pBufferStart = m_pBuffer;
- }
- else
- {
- int sockerr = GetSockError();
- Log(LOGDEBUG, "%s, recv returned %d. GetSockError(): %d (%s)", __FUNCTION__, nBytes,
- sockerr, strerror(sockerr));
- if (sockerr == EINTR && !bCtrlC)
- goto again;
-
- if (sockerr == EWOULDBLOCK || sockerr == EAGAIN)
- m_bTimedout = true;
- else
- Close();
- return false;
- }
-
- return true;
-}
+++ /dev/null
-#ifndef __RTMP_H__
-#define __RTMP_H__
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- * Copyright (C) 2009 Howard Chu
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-//#include <string>
-//#include <vector>
-
-#ifdef WIN32
-#include <winsock.h>
-#else
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <errno.h>
-#endif
-
-#include "log.h"
-
-#ifdef CRYPTO
-#include "dh.h"
-#endif
-
-#include "AMFObject.h"
-#include "rtmppacket.h"
-
-#define RTMP_PROTOCOL_UNDEFINED -1
-#define RTMP_PROTOCOL_RTMP 0
-#define RTMP_PROTOCOL_RTMPT 1 // not yet supported
-#define RTMP_PROTOCOL_RTMPS 2 // not yet supported
-#define RTMP_PROTOCOL_RTMPE 3
-#define RTMP_PROTOCOL_RTMPTE 4 // not yet supported
-#define RTMP_PROTOCOL_RTMFP 5 // not yet supported
-
-#define RTMP_DEFAULT_CHUNKSIZE 128
-
-extern char RTMPProtocolStringsLower[][7];
-
-int32_t GetTime();
-
-inline int GetSockError() {
-#ifdef WIN32
- return WSAGetLastError();
-#else
- return errno;
-#endif
-}
-
-namespace RTMP_LIB
-{
-
-typedef struct
-{
- const char *hostname;
- unsigned int port;
- int protocol;
- const char *playpath;
-
- const char *tcUrl;
- const char *swfUrl;
- const char *pageUrl;
- const char *app;
- const char *auth;
- const char *SWFHash;
- uint32_t SWFSize;
- const char *flashVer;
- const char *subscribepath;
-
- double seekTime;
- uint32_t length;
- bool bLiveStream;
-
- long int timeout; // number of seconds before connection times out
-
- #ifdef CRYPTO
- DH *dh; // for encryption
- RC4_KEY *rc4keyIn;
- RC4_KEY *rc4keyOut;
-
- //char SWFHashHMAC[32];
- char SWFVerificationResponse[42];
- #endif
-
- const char *sockshost;
- unsigned short socksport;
-} LNK;
-
-class CRTMP
- {
- public:
-
- CRTMP();
- virtual ~CRTMP();
-
- void SetBufferMS(int size);
- void UpdateBufferMS();
-
- void SetupStream(
- int protocol,
- const char *hostname,
- unsigned int port,
- const char *sockshost,
- const char *playpath,
- const char *tcUrl,
- const char *swfUrl,
- const char *pageUrl,
- const char *app,
- const char *auth,
- const char *swfSHA256Hash,
- uint32_t swfSize,
- const char *flashVer,
- const char *subscribepath,
- double dTime,
- uint32_t dLength,
- bool bLiveStream,
- long int timeout=300);
-
- bool Connect();
-
- bool IsConnected();
- bool IsTimedout();
- double GetDuration();
- bool ToggleStream();
-
- bool ConnectStream(double seekTime=-10.0, uint32_t dLength=0);
- bool ReconnectStream(int bufferTime, double seekTime=-10.0, uint32_t dLength=0);
- void DeleteStream();
- int GetNextMediaPacket(RTMPPacket &packet);
-
- void Close();
-
- static int EncodeString(char *output, const std::string &strValue);
- static int EncodeNumber(char *output, double dVal);
- static int EncodeInt16(char *output, short nVal);
- static int EncodeInt24(char *output, int nVal);
- static int EncodeInt32(char *output, int nVal);
- static int EncodeBoolean(char *output,bool bVal);
-
- static unsigned short ReadInt16(const char *data);
- static unsigned int ReadInt24(const char *data);
- static unsigned int ReadInt32(const char *data);
- static std::string ReadString(const char *data);
- static bool ReadBool(const char *data);
- static double ReadNumber(const char *data);
- bool SendPause(bool DoPause, double dTime);
-
- static bool DumpMetaData(AMFObject &obj);
- static bool FindFirstMatchingProperty(AMFObject &obj, std::string name, AMFObjectProperty &p);
-
- protected:
- bool HandShake(bool FP9HandShake=true);
- bool RTMPConnect();
- bool SocksNegotiate();
-
- bool SendConnectPacket();
- bool SendServerBW();
- bool SendCheckBW();
- bool SendCheckBWResult(double txn);
- bool SendCtrl(short nType, unsigned int nObject, unsigned int nTime = 0);
- bool SendBGHasStream(double dId, char *playpath);
- bool SendCreateStream(double dStreamId);
- bool SendDeleteStream(double dStreamId);
- bool SendFCSubscribe(const char *subscribepath);
- bool SendPlay();
- bool SendSeek(double dTime);
- bool SendBytesReceived();
-
- int HandlePacket(RTMPPacket &packet);
- int HandleInvoke(const char *body, unsigned int nBodySize);
- bool HandleMetadata(char *body, unsigned int len);
- void HandleChangeChunkSize(const RTMPPacket &packet);
- void HandleAudio(const RTMPPacket &packet);
- void HandleVideo(const RTMPPacket &packet);
- void HandleCtrl(const RTMPPacket &packet);
- void HandleServerBW(const RTMPPacket &packet);
- void HandleClientBW(const RTMPPacket &packet);
-
- int EncodeString(char *output, const std::string &strName, const std::string &strValue);
- int EncodeNumber(char *output, const std::string &strName, double dVal);
- int EncodeBoolean(char *output, const std::string &strName, bool bVal);
-
- bool SendRTMP(RTMPPacket &packet);
-
- bool ReadPacket(RTMPPacket &packet);
- int ReadN(char *buffer, int n);
- bool WriteN(const char *buffer, int n);
-
- bool FillBuffer();
- void FlushBuffer();
-
- int m_socket;
- int m_chunkSize;
- int m_nBWCheckCounter;
- int m_nBytesIn;
- int m_nBytesInSent;
- int m_nBufferMS;
- int m_stream_id; // returned in _result from invoking createStream
- int m_mediaChannel;
- uint32_t m_mediaStamp;
- uint32_t m_pauseStamp;
- int m_bPausing;
- int m_nServerBW;
- int m_nClientBW;
- uint8_t m_nClientBW2;
- bool m_bPlaying;
- bool m_bTimedout;
-
- //std::string m_strPlayer;
- //std::string m_strPageUrl;
- //std::string m_strLink;
- //std::string m_strPlayPath;
-
- std::vector<std::string> m_methodCalls; //remote method calls queue
-
- LNK Link;
- char *m_pBuffer; // data read from socket
- char *m_pBufferStart; // pointer into m_pBuffer of next byte to process
- int m_nBufferSize; // number of unprocessed bytes in buffer
- RTMPPacket *m_vecChannelsIn[65600];
- RTMPPacket *m_vecChannelsOut[65600];
- int m_channelTimestamp[65600]; // abs timestamp of last packet
-
- double m_fAudioCodecs; // audioCodecs for the connect packet
- double m_fVideoCodecs; // videoCodecs for the connect packet
-
- double m_fDuration; // duration of stream in seconds
- };
-};
-
-#endif
+++ /dev/null
-/* RTMPDump
- * Copyright (C) 2009 Andrej Stepanchuk
- * Copyright (C) 2009 Howard Chu
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#define _FILE_OFFSET_BITS 64
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include <signal.h> // to catch Ctrl-C
-#include <getopt.h>
-
-#ifdef WIN32
-#define fseeko fseeko64
-#define ftello ftello64
-#include <winsock.h>
-#include <stdio.h>
-#include <io.h>
-#include <fcntl.h>
-#define SET_BINMODE(f) setmode(fileno(f), O_BINARY)
-#else
-#define SET_BINMODE(f)
-#endif
-
-#include "rtmp.h"
-#include "log.h"
-#include "AMFObject.h"
-#include "parseurl.h"
-
-int debuglevel = 1;
-
-using namespace RTMP_LIB;
-
-#define RTMPDUMP_VERSION "v1.9"
-
-#define RD_SUCCESS 0
-#define RD_FAILED 1
-#define RD_INCOMPLETE 2
-
-// starts sockets
-bool InitSockets()
-{
-#ifdef WIN32
- WORD version;
- WSADATA wsaData;
-
- version = MAKEWORD(1,1);
- return (WSAStartup(version, &wsaData)==0);
-#else
- return true;
-#endif
-}
-
-inline void CleanupSockets() {
-#ifdef WIN32
- WSACleanup();
-#endif
-}
-
-//uint32_t nTimeStamp = 0;
-
-#ifdef _DEBUG
-uint32_t debugTS = 0;
-int pnum=0;
-
-FILE *netstackdump = 0;
-FILE *netstackdump_read = 0;
-#endif
-
-uint32_t nIgnoredFlvFrameCounter = 0;
-uint32_t nIgnoredFrameCounter = 0;
-#define MAX_IGNORED_FRAMES 50
-
-FILE *file = 0;
-bool bCtrlC = false;
-
-void sigIntHandler(int sig) {
- bCtrlC = true;
- LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
- // ignore all these signals now and let the connection close
- signal(SIGHUP, SIG_IGN);
- signal(SIGINT, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
-}
-
-int WriteHeader(
- char **buf, // target pointer, maybe preallocated
- unsigned int len // length of buffer if preallocated
- )
-{
- char flvHeader[] = { 'F', 'L', 'V', 0x01,
- 0x05, // video + audio, we finalize later if the value is different
- 0x00, 0x00, 0x00, 0x09,
- 0x00, 0x00, 0x00, 0x00 // first prevTagSize=0
- };
-
- unsigned int size = sizeof(flvHeader);
-
- if(size > len) {
- *buf = (char *)realloc(*buf, size);
- if(*buf == 0) {
- Log(LOGERROR, "Couldn't reallocate memory!");
- return -1; // fatal error
- }
- }
- memcpy(*buf, flvHeader, sizeof(flvHeader));
- return size;
-}
-
-// Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media packets, 0 if ignorable error, >0 if there is a media packet
-int WriteStream(
- CRTMP* rtmp,
- char **buf, // target pointer, maybe preallocated
- unsigned int len, // length of buffer if preallocated
- uint32_t *tsm, // pointer to timestamp, will contain timestamp of last video packet returned
- bool bResume, // resuming mode, will not write FLV header and compare metaHeader and first kexframe
- bool bLiveStream, // live mode, will not report absolute timestamps
- uint32_t nResumeTS, // resume keyframe timestamp
- char *metaHeader, // pointer to meta header (if bResume == TRUE)
- uint32_t nMetaHeaderSize, // length of meta header, if zero meta header check omitted (if bResume == TRUE)
- char *initialFrame, // pointer to initial keyframe (no FLV header or tagSize, raw data) (if bResume == TRUE)
- uint8_t initialFrameType, // initial frame type (audio or video)
- uint32_t nInitialFrameSize, // length of initial frame in bytes, if zero initial frame check omitted (if bResume == TRUE)
- uint8_t *dataType // whenever we get a video/audio packet we set an appropriate flag here, this will be later written to the FLV header
- )
-{
- static bool bStopIgnoring = false;
- static bool bFoundKeyframe = false;
- static bool bFoundFlvKeyframe = false;
-
- uint32_t prevTagSize = 0;
- int rtnGetNextMediaPacket = 0;
- RTMPPacket packet;
-
- rtnGetNextMediaPacket = rtmp->GetNextMediaPacket(packet);
- if(rtnGetNextMediaPacket)
- {
- char *packetBody = packet.m_body;
- unsigned int nPacketLen = packet.m_nBodySize;
-
- // Return -3 if this was completed nicely with invoke message Play.Stop or Play.Complete
- if (rtnGetNextMediaPacket == 2) {
- Log(LOGDEBUG, "Got Play.Complete or Play.Stop from server. Assuming stream is complete");
- return -3;
- }
-
- // skip video info/command packets
- if(packet.m_packetType == 0x09 &&
- nPacketLen == 2 &&
- ((*packetBody & 0xf0) == 0x50)) {
- return 0;
- }
-
- if(packet.m_packetType == 0x09 && nPacketLen <= 5) {
- Log(LOGWARNING, "ignoring too small video packet: size: %d", nPacketLen);
- return 0;
- }
- if(packet.m_packetType == 0x08 && nPacketLen <= 1) {
- Log(LOGWARNING, "ignoring too small audio packet: size: %d", nPacketLen);
- return 0;
- }
-#ifdef _DEBUG
- Log(LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, packet.m_hasAbsTimestamp);
- if(packet.m_packetType == 0x09)
- Log(LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0));
-#endif
-
- // check the header if we get one
- if(bResume && packet.m_nTimeStamp == 0) {
- if(nMetaHeaderSize > 0 && packet.m_packetType == 0x12) {
-
- RTMP_LIB::AMFObject metaObj;
- int nRes = metaObj.Decode(packetBody, nPacketLen);
- if(nRes >= 0) {
- std::string metastring = metaObj.GetProperty(0).GetString();
-
- if(metastring == "onMetaData") {
- // compare
- if((nMetaHeaderSize != nPacketLen) ||
- (memcmp(metaHeader, packetBody, nMetaHeaderSize) != 0)) {
- return -2;
- }
- }
- }
- }
-
- // check first keyframe to make sure we got the right position in the stream!
- // (the first non ignored frame)
- if(nInitialFrameSize > 0) {
-
- // video or audio data
- if(packet.m_packetType == initialFrameType && nInitialFrameSize == nPacketLen) {
- // we don't compare the sizes since the packet can contain several FLV packets, just make
- // sure the first frame is our keyframe (which we are going to rewrite)
- if(memcmp(initialFrame, packetBody, nInitialFrameSize) == 0) {
- Log(LOGDEBUG, "Checked keyframe successfully!");
- bFoundKeyframe = true;
- return 0; // ignore it! (what about audio data after it? it is handled by ignoring all 0ms frames, see below)
- }
- }
-
- // hande FLV streams, even though the server resends the keyframe as an extra video packet
- // it is also included in the first FLV stream chunk and we have to compare it and
- // filter it out !!
- //
- if(packet.m_packetType == 0x16) {
- // basically we have to find the keyframe with the correct TS being nResumeTS
- unsigned int pos=0;
- uint32_t ts = 0;
-
- while(pos+11 < nPacketLen) {
- uint32_t dataSize = CRTMP::ReadInt24(packetBody+pos+1); // size without header (11) and prevTagSize (4)
- ts = CRTMP::ReadInt24(packetBody+pos+4);
- ts |= (packetBody[pos+7]<<24);
-
-#ifdef _DEBUG
- Log(LOGDEBUG, "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
- packetBody[pos], dataSize, ts);
-#endif
- // ok, is it a keyframe!!!: well doesn't work for audio!
- if(packetBody[pos /*6928, test 0*/] == initialFrameType /* && (packetBody[11]&0xf0) == 0x10*/) {
- if(ts == nResumeTS) {
- Log(LOGDEBUG, "Found keyframe with resume-keyframe timestamp!");
- if(nInitialFrameSize != dataSize || memcmp(initialFrame, packetBody+pos+11, nInitialFrameSize) != 0) {
- Log(LOGERROR, "FLV Stream: Keyframe doesn't match!");
- return -2;
- }
- bFoundFlvKeyframe = true;
-
- // ok, skip this packet
- // check whether skipable:
- if(pos+11+dataSize+4 > nPacketLen) {
- Log(LOGWARNING, "Non skipable packet since it doesn't end with chunk, stream corrupt!");
- return -2;
- }
- packetBody += (pos+11+dataSize+4);
- nPacketLen -= (pos+11+dataSize+4);
-
- goto stopKeyframeSearch;
-
- } else if(nResumeTS < ts) {
- goto stopKeyframeSearch; // the timestamp ts will only increase with further packets, wait for seek
- }
- }
- pos += (11+dataSize+4);
- }
- if(ts < nResumeTS) {
- Log(LOGERROR, "First packet does not contain keyframe, all timestamps are smaller than the keyframe timestamp, so probably the resume seek failed?");
- }
-stopKeyframeSearch:
- ;
- if(!bFoundFlvKeyframe) {
- Log(LOGERROR, "Couldn't find the seeked keyframe in this chunk!");
- return 0;
- }
- }
- }
- }
-
- if(bResume && packet.m_nTimeStamp > 0 && (bFoundFlvKeyframe || bFoundKeyframe)) {
- // another problem is that the server can actually change from 09/08 video/audio packets to an FLV stream
- // or vice versa and our keyframe check will prevent us from going along with the new stream if we resumed
- //
- // in this case set the 'found keyframe' variables to true
- // We assume that if we found one keyframe somewhere and were already beyond TS > 0 we have written
- // data to the output which means we can accept all forthcoming data inclusing the change between 08/09 <-> FLV
- // packets
- bFoundFlvKeyframe = true;
- bFoundKeyframe = true;
- }
-
- // skip till we find out keyframe (seeking might put us somewhere before it)
- if(bResume && !bFoundKeyframe && packet.m_packetType != 0x16) {
- Log(LOGWARNING, "Stream does not start with requested frame, ignoring data... ");
- nIgnoredFrameCounter++;
- if(nIgnoredFrameCounter > MAX_IGNORED_FRAMES)
- return -2; // fatal error, couldn't continue stream
- return 0;
- }
- // ok, do the same for FLV streams
- if(bResume && !bFoundFlvKeyframe && packet.m_packetType == 0x16) {
- Log(LOGWARNING, "Stream does not start with requested FLV frame, ignoring data... ");
- nIgnoredFlvFrameCounter++;
- if(nIgnoredFlvFrameCounter > MAX_IGNORED_FRAMES)
- return -2;
- return 0;
- }
-
- // if bResume, we continue a stream, we have to ignore the 0ms frames since these are the first keyframes, we've got these
- // so don't mess around with multiple copies sent by the server to us! (if the keyframe is found at a later position
- // there is only one copy and it will be ignored by the preceding if clause)
- if(!bStopIgnoring && bResume && packet.m_packetType != 0x16) { // exclude type 0x16 (FLV) since it can conatin several FLV packets
- if(packet.m_nTimeStamp == 0) {
- return 0;
- } else {
- bStopIgnoring = true; // stop ignoring packets
- }
- }
-
- // calculate packet size and reallocate buffer if necessary
- unsigned int size = nPacketLen
- + ((packet.m_packetType == 0x08 || packet.m_packetType == 0x09 || packet.m_packetType == 0x12) ? 11 : 0)
- + (packet.m_packetType != 0x16 ? 4 : 0);
-
- if(size+4 > len) { // the extra 4 is for the case of an FLV stream without a last prevTagSize (we need extra 4 bytes to append it)
- *buf = (char *)realloc(*buf, size+4);
- if(*buf == 0) {
- Log(LOGERROR, "Couldn't reallocate memory!");
- return -1; // fatal error
- }
- }
- char *ptr = *buf;
-
- uint32_t nTimeStamp = 0; // use to return timestamp of last processed packet
-
- // audio (0x08), video (0x09) or metadata (0x12) packets :
- // construct 11 byte header then add rtmp packet's data
- if(packet.m_packetType == 0x08 || packet.m_packetType == 0x09 || packet.m_packetType == 0x12)
- {
- // set data type
- *dataType |= (((packet.m_packetType == 0x08)<<2)|(packet.m_packetType == 0x09));
-
- nTimeStamp = nResumeTS + packet.m_nTimeStamp;
- prevTagSize = 11 + nPacketLen;
-
- *ptr = packet.m_packetType;
- ptr++;
- ptr += CRTMP::EncodeInt24(ptr, nPacketLen);
-
- /*if(packet.m_packetType == 0x09) { // video
-
- // H264 fix:
- if((packetBody[0] & 0x0f) == 7) { // CodecId = H264
- uint8_t packetType = *(packetBody+1);
-
- uint32_t ts = CRTMP::ReadInt24(packetBody+2); // composition time
- int32_t cts = (ts+0xff800000)^0xff800000;
- Log(LOGDEBUG, "cts : %d\n", cts);
-
- nTimeStamp -= cts;
- // get rid of the composition time
- CRTMP::EncodeInt24(packetBody+2, 0);
- }
- Log(LOGDEBUG, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp, nTimeStamp);
- }*/
-
- ptr += CRTMP::EncodeInt24(ptr, nTimeStamp);
- *ptr = (char)((nTimeStamp & 0xFF000000) >> 24);
- ptr++;
-
- // stream id
- ptr += CRTMP::EncodeInt24(ptr, 0);
- }
-
- memcpy(ptr, packetBody, nPacketLen);
- unsigned int len = nPacketLen;
-
- // correct tagSize and obtain timestamp if we have an FLV stream
- if(packet.m_packetType == 0x16)
- {
- unsigned int pos=0;
-
- while(pos+11 < nPacketLen)
- {
- uint32_t dataSize = CRTMP::ReadInt24(packetBody+pos+1); // size without header (11) and without prevTagSize (4)
- nTimeStamp = CRTMP::ReadInt24(packetBody+pos+4);
- nTimeStamp |= (packetBody[pos+7]<<24);
-
- /*
- CRTMP::EncodeInt24(ptr+pos+4, nTimeStamp);
- ptr[pos+7] = (nTimeStamp>>24)&0xff;//*/
-
- // set data type
- *dataType |= (((*(packetBody+pos) == 0x08)<<2)|(*(packetBody+pos) == 0x09));
-
- if(pos+11+dataSize+4 > nPacketLen) {
- if(pos+11+dataSize > nPacketLen) {
- Log(LOGERROR, "Wrong data size (%lu), stream corrupted, aborting!", dataSize);
- return -2;
- }
- Log(LOGWARNING, "No tagSize found, appending!");
-
- // we have to append a last tagSize!
- prevTagSize = dataSize+11;
- CRTMP::EncodeInt32(ptr+pos+11+dataSize, prevTagSize);
- size+=4; len+=4;
- } else {
- prevTagSize = CRTMP::ReadInt32(packetBody+pos+11+dataSize);
-
-#ifdef _DEBUG
- Log(LOGDEBUG, "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
- (unsigned char)packetBody[pos], dataSize, prevTagSize, nTimeStamp);
-#endif
-
- if(prevTagSize != (dataSize+11)) {
-#ifdef _DEBUG
- Log(LOGWARNING, "Tag and data size are not consitent, writing tag size according to dataSize+11: %d", dataSize+11);
-#endif
-
- prevTagSize = dataSize+11;
- CRTMP::EncodeInt32(ptr+pos+11+dataSize, prevTagSize);
- }
- }
-
- pos += prevTagSize+4;//(11+dataSize+4);
- }
- }
- ptr += len;
-
- if(packet.m_packetType != 0x16) { // FLV tag packets contain their own prevTagSize
- CRTMP::EncodeInt32(ptr, prevTagSize);
- //ptr += 4;
- }
-
- // In non-live this nTimeStamp can contain an absolute TS.
- // Update ext timestamp with this absolute offset in non-live mode otherwise report the relative one
- // LogPrintf("\nDEBUG: type: %02X, size: %d, pktTS: %dms, TS: %dms, bLiveStream: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, nTimeStamp, bLiveStream);
- if(tsm)
- *tsm = bLiveStream ? packet.m_nTimeStamp : nTimeStamp;
-
-
- return size;
- }
-
- return -1; // no more media packets
-}
-
-int OpenResumeFile(const char *flvFile, // file name [in]
- FILE **file, // opened file [out]
- off_t *size, // size of the file [out]
- char **metaHeader, // meta data read from the file [out]
- uint32_t *nMetaHeaderSize, // length of metaHeader [out]
- double *duration) // duration of the stream in ms [out]
-{
- const size_t bufferSize = 1024;
- char buffer[bufferSize];
-
- *nMetaHeaderSize = 0;
- *size = 0;
-
- *file = fopen(flvFile, "r+b");
- if (!*file)
- return RD_SUCCESS; // RD_SUCCESS, because we go to fresh file mode instead of quiting
-
- fseek(*file, 0, SEEK_END);
- *size = ftello(*file);
- fseek(*file, 0, SEEK_SET);
-
- if(*size > 0) {
- // verify FLV format and read header
- uint32_t prevTagSize = 0;
-
- // check we've got a valid FLV file to continue!
- if(fread(buffer, 1, 13, *file) != 13) {
- Log(LOGERROR, "Couldn't read FLV file header!");
- return RD_FAILED;
- }
- if(buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' || buffer[3] != 0x01) {
- Log(LOGERROR, "Inavlid FLV file!");
- return RD_FAILED;
- }
-
- if((buffer[4]&0x05) == 0) {
- Log(LOGERROR, "FLV file contains neither video nor audio, aborting!");
- return RD_FAILED;
- }
-
- uint32_t dataOffset = RTMP_LIB::CRTMP::ReadInt32(buffer+5);
- fseek(*file, dataOffset, SEEK_SET);
-
- if(fread(buffer, 1, 4, *file) != 4) {
- Log(LOGERROR, "Invalid FLV file: missing first prevTagSize!");
- return RD_FAILED;
- }
- prevTagSize = RTMP_LIB::CRTMP::ReadInt32(buffer);
- if(prevTagSize != 0) {
- Log(LOGWARNING, "First prevTagSize is not zero: prevTagSize = 0x%08X", prevTagSize);
- }
-
- // go through the file to find the meta data!
- off_t pos = dataOffset+4;
- bool bFoundMetaHeader = false;
-
- while(pos < *size-4 && !bFoundMetaHeader) {
- fseeko(*file, pos, SEEK_SET);
- if(fread(buffer, 1, 4, *file)!=4)
- break;
-
- uint32_t dataSize = RTMP_LIB::CRTMP::ReadInt24(buffer+1);
-
- if(buffer[0] == 0x12) {
- if (dataSize > bufferSize) {
- Log(LOGERROR, "%s: dataSize (%d) > bufferSize (%d)", __FUNCTION__, dataSize, bufferSize);
- return RD_FAILED;
- }
-
- fseeko(*file, pos+11, SEEK_SET);
- if(fread(buffer, 1, dataSize, *file) != dataSize)
- break;
-
- RTMP_LIB::AMFObject metaObj;
- int nRes = metaObj.Decode(buffer, dataSize);
- if(nRes < 0) {
- Log(LOGERROR, "%s, error decoding meta data packet", __FUNCTION__);
- break;
- }
-
- std::string metastring = metaObj.GetProperty(0).GetString();
-
- if(metastring == "onMetaData") {
- metaObj.Dump();
-
- *nMetaHeaderSize = dataSize;
- if (*metaHeader) free(*metaHeader);
- *metaHeader = (char *)malloc(*nMetaHeaderSize);
- memcpy(*metaHeader, buffer, *nMetaHeaderSize);
-
- // get duration
- AMFObjectProperty prop;
- if(RTMP_LIB::CRTMP::FindFirstMatchingProperty(metaObj, "duration", prop)) {
- *duration = prop.GetNumber();
- Log(LOGDEBUG, "File has duration: %f", *duration);
- }
-
- bFoundMetaHeader = true;
- break;
- }
- //metaObj.Reset();
- //delete obj;
- }
- pos += (dataSize+11+4);
- }
-
- if(!bFoundMetaHeader)
- Log(LOGWARNING, "Couldn't locate meta data!");
- }
-
- return RD_SUCCESS;
-}
-
-int GetLastKeyframe(FILE *file, // output file [in]
- int nSkipKeyFrames, // max number of frames to skip when searching for key frame [in]
- uint32_t *dSeek, // offset of the last key frame [out]
- char **initialFrame, // content of the last keyframe [out]
- int *initialFrameType, // initial frame type (audio/video) [out]
- uint32_t *nInitialFrameSize) // length of initialFrame [out]
-{
- const size_t bufferSize = 16;
- char buffer[bufferSize];
- uint8_t dataType;
- bool bAudioOnly;
- off_t size;
-
- fseek(file, 0, SEEK_END);
- size = ftello(file);
-
- fseek(file, 4, SEEK_SET);
- fread(&dataType, sizeof(uint8_t), 1, file);
- bAudioOnly = (dataType & 0x4) && !(dataType & 0x1);
-
- Log(LOGDEBUG, "bAudioOnly: %d, size: %llu", bAudioOnly, (unsigned long long)size);
-
- // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
-
- //if(!bAudioOnly) // we have to handle video/video+audio different since we have non-seekable frames
- //{
- // find the last seekable frame
- off_t tsize = 0;
- uint32_t prevTagSize = 0;
-
- // go through the file and find the last video keyframe
- do {
- int xread;
-skipkeyframe:
- if(size-tsize < 13) {
- Log(LOGERROR, "Unexpected start of file, error in tag sizes, couldn't arrive at prevTagSize=0");
- return RD_FAILED;
- }
- fseeko(file, size-tsize-4, SEEK_SET);
- xread = fread(buffer, 1, 4, file);
- if(xread != 4) {
- Log(LOGERROR, "Couldn't read prevTagSize from file!");
- return RD_FAILED;
- }
-
- prevTagSize = RTMP_LIB::CRTMP::ReadInt32(buffer);
- //Log(LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);
-
- if(prevTagSize == 0) {
- Log(LOGERROR, "Couldn't find keyframe to resume from!");
- return RD_FAILED;
- }
-
- if(prevTagSize < 0 || prevTagSize > size-4-13) {
- Log(LOGERROR, "Last tag size must be greater/equal zero (prevTagSize=%d) and smaller then filesize, corrupt file!", prevTagSize);
- return RD_FAILED;
- }
- tsize += prevTagSize+4;
-
- // read header
- fseeko(file, size-tsize, SEEK_SET);
- if(fread(buffer, 1, 12, file) != 12) {
- Log(LOGERROR, "Couldn't read header!");
- return RD_FAILED;
- }
- //*
-#ifdef _DEBUG
- uint32_t ts = RTMP_LIB::CRTMP::ReadInt24(buffer+4);
- ts |= (buffer[7]<<24);
- Log(LOGDEBUG, "%02X: TS: %d ms", buffer[0], ts);
-#endif //*/
-
- // this just continues the loop whenever the number of skipped frames is > 0,
- // so we look for the next keyframe to continue with
- //
- // this helps if resuming from the last keyframe fails and one doesn't want to start
- // the download from the beginning
- //
- if(nSkipKeyFrames > 0 && !(!bAudioOnly && (buffer[0] != 0x09 || (buffer[11]&0xf0) != 0x10))) {
- #ifdef _DEBUG
- Log(LOGDEBUG, "xxxxxxxxxxxxxxxxxxxxxxxx Well, lets go one more back!");
- #endif
- nSkipKeyFrames--;
- goto skipkeyframe;
- }
-
- } while(
- (bAudioOnly && buffer[0] != 0x08) ||
- (!bAudioOnly && (buffer[0] != 0x09 || (buffer[11]&0xf0) != 0x10))
- ); // as long as we don't have a keyframe / last audio frame
-
- // save keyframe to compare/find position in stream
- *initialFrameType = buffer[0];
- *nInitialFrameSize = prevTagSize-11;
- *initialFrame = (char *)malloc(*nInitialFrameSize);
-
- fseeko(file, size-tsize+11, SEEK_SET);
- if(fread(*initialFrame, 1, *nInitialFrameSize, file) != *nInitialFrameSize) {
- Log(LOGERROR, "Couldn't read last keyframe, aborting!");
- return RD_FAILED;
- }
-
- *dSeek = RTMP_LIB::CRTMP::ReadInt24(buffer+4); // set seek position to keyframe tmestamp
- *dSeek |= (buffer[7]<<24);
- //}
- //else // handle audio only, we can seek anywhere we'd like
- //{
- //}
-
- if(*dSeek < 0) {
- Log(LOGERROR, "Last keyframe timestamp is negative, aborting, your file is corrupt!");
- return RD_FAILED;
- }
- Log(LOGDEBUG,"Last keyframe found at: %d ms, size: %d, type: %02X", *dSeek, *nInitialFrameSize, *initialFrameType);
-
- /*
- // now read the timestamp of the frame before the seekable keyframe:
- fseeko(file, size-tsize-4, SEEK_SET);
- if(fread(buffer, 1, 4, file) != 4) {
- Log(LOGERROR, "Couldn't read prevTagSize from file!");
- goto start;
- }
- uint32_t prevTagSize = RTMP_LIB::CRTMP::ReadInt32(buffer);
- fseeko(file, size-tsize-4-prevTagSize+4, SEEK_SET);
- if(fread(buffer, 1, 4, file) != 4) {
- Log(LOGERROR, "Couldn't read previous timestamp!");
- goto start;
- }
- uint32_t timestamp = RTMP_LIB::CRTMP::ReadInt24(buffer);
- timestamp |= (buffer[3]<<24);
-
- Log(LOGDEBUG, "Previuos timestamp: %d ms", timestamp);
- */
-
- if(*dSeek != 0) {
- // seek to position after keyframe in our file (we will ignore the keyframes resent by the server
- // since they are sent a couple of times and handling this would be a mess)
- fseeko(file, size-tsize+prevTagSize+4, SEEK_SET);
-
- // make sure the WriteStream doesn't write headers and ignores all the 0ms TS packets
- // (including several meta data headers and the keyframe we seeked to)
- //bNoHeader = true; if bResume==true this is true anyway
- }
-
- //}
-
- return RD_SUCCESS;
-}
-
-int Download(CRTMP *rtmp, // connected CRTMP object
- FILE *file,
- uint32_t dSeek,
- uint32_t dLength,
- double duration,
- bool bResume,
- char *metaHeader,
- uint32_t nMetaHeaderSize,
- char *initialFrame,
- int initialFrameType,
- uint32_t nInitialFrameSize,
- int nSkipKeyFrames,
- bool bStdoutMode,
- bool bLiveStream,
- bool bHashes,
- bool bOverrideBufferTime,
- uint32_t bufferTime,
- double *percent) // percentage downloaded [out]
-{
- uint32_t timestamp = dSeek;
- int32_t now, lastUpdate;
- uint8_t dataType = 0; // will be written into the FLV header (position 4)
- int bufferSize = 1024*1024;
- char *buffer = (char *)malloc(bufferSize);
- int nRead = 0;
- off_t size = ftello(file);
- unsigned long lastPercent = 0;
-
- memset(buffer, 0, bufferSize);
-
- *percent = 0.0;
-
- if(timestamp) {
- Log(LOGDEBUG, "Continuing at TS: %d ms\n", timestamp);
- }
-
- if(bLiveStream) {
- LogPrintf("Starting Live Stream\n");
- } else {
- // print initial status
- // Workaround to exit with 0 if the file is fully (> 99.9%) downloaded
- if( duration > 0 ) {
- if ((double)timestamp >= (double)duration*999.0 ) {
- LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n", (double)timestamp/1000.0, (double)duration/1000.0);
- return RD_SUCCESS;
- } else {
- *percent = ((double)timestamp) / (duration*1000.0)*100.0;
- *percent = round(*percent*10.0)/10.0;
- LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n",
- bResume ? "Resuming":"Starting",
- (double)size/1024.0, (double)timestamp/1000.0, *percent);
- }
- } else {
- LogPrintf("%s download at: %.3f kB\n", bResume ? "Resuming":"Starting",(double)size/1024.0);
- }
- }
-
- if (dLength > 0)
- LogPrintf("For duration: %.3f sec\n", (double)dLength/1000.0);
-
- // write FLV header if not resuming
- if(!bResume) {
- nRead = WriteHeader(&buffer, bufferSize);
- if(nRead > 0) {
- if(fwrite(buffer, sizeof(unsigned char), nRead, file) != (size_t)nRead) {
- Log(LOGERROR, "%s: Failed writing FLV header, exiting!", __FUNCTION__);
- free(buffer);
- return RD_FAILED;
- }
- size += nRead;
- } else {
- Log(LOGERROR, "Couldn't obtain FLV header, exiting!");
- free(buffer);
- return RD_FAILED;
- }
- }
-
- now = GetTime();
- lastUpdate = now-1000;
- do
- {
- nRead = WriteStream(rtmp, &buffer, bufferSize, ×tamp, bResume && nInitialFrameSize > 0, bLiveStream, dSeek, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, &dataType);
-
- //LogPrintf("nRead: %d\n", nRead);
- if(nRead > 0) {
- if(fwrite(buffer, sizeof(unsigned char), nRead, file) != (size_t)nRead) {
- Log(LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__);
- free(buffer);
- return RD_FAILED;
- }
- size += nRead;
-
- //LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0);
- if(duration <= 0) // if duration unknown try to get it from the stream (onMetaData)
- duration = rtmp->GetDuration();
-
- if(duration > 0) {
- // make sure we claim to have enough buffer time!
- if(!bOverrideBufferTime && bufferTime < (duration*1000.0)) {
- bufferTime = (uint32_t)(duration*1000.0)+5000; // extra 5sec to make sure we've got enough
-
- Log(LOGDEBUG, "Detected that buffer time is less than duration, resetting to: %dms", bufferTime);
- rtmp->SetBufferMS(bufferTime);
- rtmp->UpdateBufferMS();
- }
- *percent = ((double)timestamp) / (duration*1000.0)*100.0;
- *percent = round(*percent*10.0)/10.0;
- if (bHashes) {
- if ( lastPercent + 1 <= *percent ) {
- LogStatus("#");
- lastPercent = (unsigned long)*percent;
- }
- } else {
- now = GetTime();
- if (abs(now - lastUpdate) > 200) {
- LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", (double)size/1024.0, (double)(timestamp)/1000.0, *percent);
- lastUpdate = now;
- }
- }
- } else {
- now = GetTime();
- if (abs(now - lastUpdate) > 200) {
- if (bHashes)
- LogStatus("#");
- else
- LogStatus("\r%.3f kB / %.2f sec", (double)size/1024.0, (double)(timestamp)/1000.0);
- lastUpdate = now;
- }
- }
- }
-#ifdef _DEBUG
- else { Log(LOGDEBUG, "zero read!"); }
-#endif
-
- } while(!bCtrlC && nRead > -1 && rtmp->IsConnected());
- free(buffer);
-
- Log(LOGDEBUG, "WriteStream returned: %d", nRead);
-
- if(bResume && nRead == -2) {
- LogPrintf("Couldn't resume FLV file, try --skip %d\n\n", nSkipKeyFrames+1);
- return RD_FAILED;
- }
-
- // finalize header by writing the correct dataType (video, audio, video+audio)
- if(!bResume && dataType != 0x5 && !bStdoutMode) {
- //Log(LOGDEBUG, "Writing data type: %02X", dataType);
- fseek(file, 4, SEEK_SET);
- fwrite(&dataType, sizeof(unsigned char), 1, file);
- }
-
- if(nRead == -3)
- return RD_SUCCESS;
-
- if((duration > 0 && *percent < 99.9) || bCtrlC || nRead < 0 || rtmp->IsTimedout()) {
- return RD_INCOMPLETE;
- }
-
- return RD_SUCCESS;
-}
-
-//#define _DEBUG_TEST_PLAYSTOP
-
-int main(int argc, char **argv)
-{
- extern char *optarg;
-
-//#ifdef _DEBUG_TEST_PLAYSTOP
-// RTMPPacket packet;
-//#endif
- int nStatus = RD_SUCCESS;
- double percent = 0;
- double duration = 0.0;
-
-
- int nSkipKeyFrames = 0; // skip this number of keyframes when resuming
-
- bool bOverrideBufferTime = false; // if the user specifies a buffer time override this is true
- bool bStdoutMode = true;// if true print the stream directly to stdout, messages go to stderr
- bool bResume = false; // true in resume mode
- uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise
- uint32_t bufferTime = 10*60*60*1000; // 10 hours as default
-
- // meta header and initial frame for the resume mode (they are read from the file and compared with
- // the stream we are trying to continue
- char *metaHeader = 0;
- uint32_t nMetaHeaderSize = 0;
-
- // video keyframe for matching
- char *initialFrame = 0;
- uint32_t nInitialFrameSize = 0;
- int initialFrameType = 0; // tye: audio or video
-
- char *hostname = 0;
- char *playpath = 0;
- char *subscribepath = 0;
- int port = -1;
- int protocol = RTMP_PROTOCOL_UNDEFINED;
- bool bLiveStream = false; // is it a live stream? then we can't seek/resume
- bool bHashes = false; // display byte counters not hashes by default
-
- long int timeout = 120; // timeout connection after 120 seconds
- uint32_t dStartOffset = 0; // seek position in non-live mode
- uint32_t dStopOffset = 0;
- uint32_t dLength = 0; // length to play from stream - calculated from seek position and dStopOffset
-
- char *rtmpurl = 0;
- char *swfUrl = 0;
- char *tcUrl = 0;
- char *pageUrl = 0;
- char *app = 0;
- char *auth = 0;
- char *swfHash = 0;
- uint32_t swfSize = 0;
- char *flashVer = 0;
- char *sockshost = 0;
-
- char *flvFile = 0;
-
- char DEFAULT_FLASH_VER[] = "LNX 10,0,22,87";
-
- signal(SIGHUP, sigIntHandler);
- signal(SIGINT, sigIntHandler);
- signal(SIGPIPE, sigIntHandler);
- signal(SIGTERM, sigIntHandler);
- signal(SIGQUIT, sigIntHandler);
-
- /* sleep(30); */
-
- // Check for --quiet option before printing any output
- int index = 0;
- while (index < argc)
- {
- if ( strcmp( argv[index], "--quiet")==0 || strcmp( argv[index], "-q")==0 )
- debuglevel = LOGCRIT;
- index++;
- }
-
- LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
- LogPrintf("(c) 2009 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
-
- int opt;
- struct option longopts[] = {
- {"help", 0, NULL, 'h'},
- {"host", 1, NULL, 'n'},
- {"port", 1, NULL, 'c'},
- {"socks", 1, NULL, 'S'},
- {"protocol",1, NULL, 'l'},
- {"playpath",1, NULL, 'y'},
- {"rtmp", 1, NULL, 'r'},
- {"swfUrl", 1, NULL, 's'},
- {"tcUrl", 1, NULL, 't'},
- {"pageUrl", 1, NULL, 'p'},
- {"app", 1, NULL, 'a'},
- {"auth", 1, NULL, 'u'},
-#ifdef CRYPTO
- {"swfhash", 1, NULL, 'w'},
- {"swfsize", 1, NULL, 'x'},
-#endif
- {"flashVer",1, NULL, 'f'},
- {"live" ,0, NULL, 'v'},
- {"flv", 1, NULL, 'o'},
- {"resume", 0, NULL, 'e'},
- {"timeout", 1, NULL, 'm'},
- {"buffer", 1, NULL, 'b'},
- {"skip", 1, NULL, 'k'},
- {"subscribe",1,NULL, 'd'},
- {"start", 1, NULL, 'A'},
- {"stop", 1, NULL, 'B'},
- {"hashes", 0, NULL, '#'},
- {"debug", 0, NULL, 'z'},
- {"quiet", 0, NULL, 'q'},
- {"verbose", 0, NULL, 'V'},
- {0,0,0,0}
- };
-
- while((opt = getopt_long(argc, argv, "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:w:x:S:#", longopts, NULL)) != -1) {
- switch(opt) {
- case 'h':
- LogPrintf("\nThis program dumps the media content streamed over rtmp.\n\n");
- LogPrintf("--help|-h Prints this help screen.\n");
- LogPrintf("--rtmp|-r url URL (e.g. rtmp//hotname[:port]/path)\n");
- LogPrintf("--host|-n hostname Overrides the hostname in the rtmp url\n");
- LogPrintf("--port|-c port Overrides the port in the rtmp url\n");
- LogPrintf("--socks|-S host:port Use the specified SOCKS proxy\n");
- LogPrintf("--protocol|-l Overrides the protocol in the rtmp url (0 - RTMP, 3 - RTMPE)\n");
- LogPrintf("--playpath|-y Overrides the playpath parsed from rtmp url\n");
- LogPrintf("--swfUrl|-s url URL to player swf file\n");
- LogPrintf("--tcUrl|-t url URL to played stream (default: \"rtmp://host[:port]/app\")\n");
- LogPrintf("--pageUrl|-p url Web URL of played programme\n");
- LogPrintf("--app|-a app Name of player used\n");
-#ifdef CRYPTO
- LogPrintf("--swfhash|-w hexstring SHA256 hash of the decompressed SWF file (32 bytes)\n");
- LogPrintf("--swfsize|-x num Size of the decompressed SWF file, required for SWFVerification\n");
-#endif
- LogPrintf("--auth|-u string Authentication string to be appended to the connect string\n");
- LogPrintf("--flashVer|-f string Flash version string (default: \"%s\")\n", DEFAULT_FLASH_VER);
- LogPrintf("--live|-v Save a live stream, no --resume (seeking) of live streams possible\n");
- LogPrintf("--subscribe|-d string Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
- LogPrintf("--flv|-o string FLV output file name, if the file name is - print stream to stdout\n");
- LogPrintf("--resume|-e Resume a partial RTMP download\n");
- LogPrintf("--timeout|-m num Timeout connection num seconds (default: %lu)\n", timeout);
- LogPrintf("--start|-A num Start at num seconds into stream (not valid when using --live)\n");
- LogPrintf("--stop|-B num Stop at num seconds into stream\n");
- LogPrintf("--hashes|-# Display progress with hashes, not with the byte counter\n");
- LogPrintf("--buffer|-b Buffer time in milliseconds (default: %lu), this option makes only sense in stdout mode (-o -)\n",
- bufferTime);
- LogPrintf("--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
- nSkipKeyFrames);
- LogPrintf("--quiet|-q Supresses all command output.\n");
- LogPrintf("--verbose|-V Verbose command output.\n");
- LogPrintf("--debug|-z Debug level command output.\n");
- LogPrintf("If you don't pass parameters for swfUrl, pageUrl, app or auth these propertiews will not be included in the connect ");
- LogPrintf("packet.\n\n");
- return RD_SUCCESS;
-#ifdef CRYPTO
- case 'w':
- {
- int res = hex2bin(optarg, &swfHash);
- if(res!=32) {
- swfHash = NULL;
- Log(LOGWARNING, "Couldn't parse swf hash hex string, not heyxstring or not 32 bytes, ignoring!");
- }
- break;
- }
- case 'x':
- {
- int size = atoi(optarg);
- if(size <= 0) {
- Log(LOGERROR, "SWF Size must be at least 1, ignoring\n");
- } else {
- swfSize = size;
- }
- break;
- }
-#endif
- case 'k':
- nSkipKeyFrames = atoi(optarg);
- if(nSkipKeyFrames < 0) {
- Log(LOGERROR, "Number of keyframes skipped must be greater or equal zero, using zero!");
- nSkipKeyFrames = 0;
- } else {
- Log(LOGDEBUG, "Number of skipped key frames for resume: %d", nSkipKeyFrames);
- }
- break;
- case 'b':
- {
- int32_t bt = atol(optarg);
- if(bt < 0) {
- Log(LOGERROR, "Buffer time must be greater than zero, ignoring the specified value %d!", bt);
- } else {
- bufferTime = bt;
- bOverrideBufferTime = true;
- }
- break;
- }
- case 'v':
- bLiveStream = true; // no seeking or resuming possible!
- break;
- case 'd':
- subscribepath = optarg;
- break;
- case 'n':
- hostname = optarg;
- break;
- case 'c':
- port = atoi(optarg);
- break;
- case 'l':
- protocol = atoi(optarg);
- if(protocol != RTMP_PROTOCOL_RTMP && protocol != RTMP_PROTOCOL_RTMPE) {
- Log(LOGERROR, "Unknown protocol specified: %d", protocol);
- return RD_FAILED;
- }
- break;
- case 'y':
- playpath = optarg;
- break;
- case 'r':
- {
- rtmpurl = optarg;
-
- char *parsedHost = 0;
- unsigned int parsedPort = 0;
- char *parsedPlaypath = 0;
- char *parsedApp = 0;
- int parsedProtocol = RTMP_PROTOCOL_UNDEFINED;
-
- if(!ParseUrl(rtmpurl, &parsedProtocol, &parsedHost, &parsedPort, &parsedPlaypath, &parsedApp)) {
- Log(LOGWARNING, "Couldn't parse the specified url (%s)!", optarg);
- } else {
- if(hostname == 0)
- hostname = parsedHost;
- if(port == -1)
- port = parsedPort;
- if(playpath == 0)
- playpath = parsedPlaypath;
- if(protocol == RTMP_PROTOCOL_UNDEFINED)
- protocol = parsedProtocol;
- if(app == 0)
- app = parsedApp;
- }
- break;
- }
- case 's':
- swfUrl = optarg;
- break;
- case 't':
- tcUrl = optarg;
- break;
- case 'p':
- pageUrl = optarg;
- break;
- case 'a':
- app = optarg;
- break;
- case 'f':
- flashVer = optarg;
- break;
- case 'o':
- flvFile = optarg;
- if(strcmp(flvFile, "-"))
- bStdoutMode = false;
-
- break;
- case 'e':
- bResume = true;
- break;
- case 'u':
- auth = optarg;
- break;
- case 'm':
- timeout = atoi(optarg);
- break;
- case 'A':
- dStartOffset = int(atof(optarg)*1000.0);
- break;
- case 'B':
- dStopOffset = int(atof(optarg)*1000.0);
- break;
- case '#':
- bHashes = true;
- break;
- case 'q':
- debuglevel = LOGCRIT;
- break;
- case 'V':
- debuglevel = LOGDEBUG;
- break;
- case 'z':
- debuglevel = LOGALL;
- break;
- case 'S':
- sockshost = optarg;
- break;
- default:
- LogPrintf("unknown option: %c\n", opt);
- break;
- }
- }
-
- if(hostname == 0) {
- Log(LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
- return RD_FAILED;
- }
- if(playpath == 0) {
- Log(LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
- return RD_FAILED;
- }
-
- if(port == -1) {
- Log(LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
- port = 1935;
- }
- if(port == 0) {
- port = 1935;
- }
- if(protocol == RTMP_PROTOCOL_UNDEFINED) {
- Log(LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
- protocol = RTMP_PROTOCOL_RTMP;
- }
- if(flvFile == 0) {
- Log(LOGWARNING, "You haven't specified an output file (-o filename), using stdout");
- bStdoutMode = true;
- }
-
- if(bStdoutMode && bResume) {
- Log(LOGWARNING, "Can't resume in stdout mode, ignoring --resume option");
- bResume = false;
- }
-
- if(bLiveStream && bResume) {
- Log(LOGWARNING, "Can't resume live stream, ignoring --resume option");
- bResume = false;
- }
-
- if(swfHash == 0 && swfSize > 0) {
- Log(LOGWARNING, "Ignoring SWF size, supply also the hash with --swfhash");
- swfSize=0;
- }
-
- if(swfHash != 0 && swfSize == 0) {
- Log(LOGWARNING, "Ignoring SWF hash, supply also the swf size with --swfsize");
- swfHash=NULL;
- }
-
- if(flashVer == 0)
- flashVer = DEFAULT_FLASH_VER;
-
- if(!InitSockets()) {
- Log(LOGERROR, "Couldn't load sockets support on your platform, exiting!");
- return RD_FAILED;
- }
-
- if(tcUrl == 0 && app != 0) {
- char str[512]={0};
-
- /*
- struct hostent *h = gethostbyname(hostname);
- if(h == NULL || h->h_addr == NULL) {
- Log(LOGERROR, "DNS name resolution failed, exiting");
- return RD_FAILED;
- }
- hostip = inet_ntoa(*(struct in_addr*)h->h_addr)
- //*/
- snprintf(str, 511, "%s://%s:%d/%s", RTMPProtocolStringsLower[protocol], hostname, port, app);
- tcUrl = (char *)malloc(strlen(str)+1);
- strcpy(tcUrl, str);
- }
-
- int first = 1;
-
- // User defined seek offset
- if (dStartOffset > 0) {
- // Live stream
- if (bLiveStream) {
- Log(LOGWARNING, "Can't seek in a live stream, ignoring --start option");
- dStartOffset = 0;
- }
- }
-
- CRTMP *rtmp = new CRTMP();
- rtmp->SetupStream(protocol, hostname, port, sockshost, playpath, tcUrl, swfUrl,
- pageUrl, app, auth, swfHash, swfSize, flashVer, subscribepath,
- dSeek, 0, bLiveStream, timeout);
-
- off_t size = 0;
-
- // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
- if(bResume) {
- nStatus = OpenResumeFile(flvFile, &file, &size, &metaHeader, &nMetaHeaderSize, &duration);
- if (nStatus == RD_FAILED)
- goto clean;
-
- if (!file) {
- // file does not exist, so go back into normal mode
- bResume = false; // we are back in fresh file mode (otherwise finalizing file won't be done)
- } else {
- nStatus = GetLastKeyframe(file, nSkipKeyFrames,
- &dSeek, &initialFrame,
- &initialFrameType,
- &nInitialFrameSize);
- if (nStatus == RD_FAILED) {
- Log(LOGDEBUG, "Failed to get last keyframe.");
- goto clean;
- }
-
- if (dSeek == 0) {
- Log(LOGDEBUG, "Last keyframe is first frame in stream, switching from resume to normal mode!");
- bResume = false;
- }
- }
- }
-
- if (!file) {
- if(bStdoutMode) {
- file = stdout;
- SET_BINMODE(file);
- } else
- {
- file = fopen(flvFile, "w+b");
- if(file == 0) {
- LogPrintf("Failed to open file! %s\n", flvFile);
- return RD_FAILED;
- }
- }
- }
-
-#ifdef _DEBUG
- netstackdump = fopen("netstackdump", "wb");
- netstackdump_read = fopen("netstackdump_read", "wb");
-#endif
-
- while (!bCtrlC) {
- Log(LOGDEBUG, "Setting buffer time to: %dms", bufferTime);
- rtmp->SetBufferMS(bufferTime);
-
- if (first) {
- first = 0;
- LogPrintf("Connecting ...\n");
-
- if (!rtmp->Connect()) {
- nStatus = RD_FAILED;
- break;
- }
-
- Log(LOGINFO, "Connected...");
-
- // User defined seek offset
- if (dStartOffset > 0) {
- // Don't need the start offset if resuming an existing file
- if (bResume) {
- Log(LOGWARNING, "Can't seek a resumed stream, ignoring --start option");
- dStartOffset = 0;
- } else {
- dSeek = dStartOffset;
- }
- }
-
- // Calculate the length of the stream to still play
- if (dStopOffset > 0) {
- dLength = dStopOffset - dSeek;
-
- // Quit if start seek is past required stop offset
- if(dLength <= 0) {
- LogPrintf("Already Completed\n");
- nStatus = RD_SUCCESS;
- break;
- }
- }
-
- if (!rtmp->ConnectStream(dSeek, dLength)) {
- nStatus = RD_FAILED;
- break;
- }
- } else {
- nInitialFrameSize = 0;
-
- Log(LOGINFO, "Connection timed out, trying to resume.\n\n");
- if (!rtmp->ToggleStream()) {
- Log(LOGERROR, "Failed to resume the stream\n\n");
- if (!rtmp->IsTimedout())
- nStatus = RD_FAILED;
- else
- nStatus = RD_INCOMPLETE;
- break;
- }
- bResume = true;
- }
-
- nStatus = Download(rtmp, file, dSeek, dLength, duration, bResume,
- metaHeader, nMetaHeaderSize, initialFrame,
- initialFrameType, nInitialFrameSize,
- nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes,
- bOverrideBufferTime, bufferTime, &percent);
- free(initialFrame);
- initialFrame = NULL;
-
- /* If we succeeded, we're done.
- */
- if (nStatus != RD_INCOMPLETE || !rtmp->IsTimedout() || bLiveStream)
- break;
- }
-
- if (nStatus == RD_SUCCESS) {
- LogPrintf("Download complete\n");
- } else if (nStatus == RD_INCOMPLETE) {
- LogPrintf("Download may be incomplete (downloaded about %.2f%%), try resuming\n", percent);
- }
-
-clean:
- Log(LOGDEBUG, "Closing connection.\n");
- rtmp->Close();
- //LogPrintf("done!\n\n");
-
- if(file != 0)
- fclose(file);
-
- CleanupSockets();
-
-#ifdef _DEBUG
- if(netstackdump != 0)
- fclose(netstackdump);
- if(netstackdump_read != 0)
- fclose(netstackdump_read);
-#endif
- delete rtmp;
- return nStatus;
-}
-
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- * Copyright (C) 2009 Howard Chu
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "rtmppacket.h"
-#include "log.h"
-
-using namespace RTMP_LIB;
-
-RTMPPacket::RTMPPacket()
-{
- Reset();
-}
-
-RTMPPacket::~RTMPPacket()
-{
- FreePacket();
-}
-
-void RTMPPacket::Reset()
-{
- m_headerType = 0;
- m_packetType = 0;
- m_nChannel = 0;
- m_nInfoField1 = 0;
- m_nInfoField2 = 0;
- m_hasAbsTimestamp = false;
- m_nBodySize = 0;
- m_nBytesRead = 0;
- m_body = NULL;
- m_buffer = NULL;
-}
-
-bool RTMPPacket::AllocPacket(int nSize)
-{
- m_buffer = (char *)calloc(1, nSize+RTMP_MAX_HEADER_SIZE);
- if (!m_buffer)
- return false;
- m_body = m_buffer+RTMP_MAX_HEADER_SIZE;
- m_nBytesRead = 0;
- return true;
-}
-
-void RTMPPacket::FreePacket()
-{
- FreePacketHeader();
- Reset();
-}
-
-void RTMPPacket::FreePacketHeader()
-{
- if (m_buffer)
- free(m_buffer);
- m_buffer = NULL;
- m_body = NULL;
-}
-
-void RTMPPacket::Dump()
-{
- Log(LOGDEBUG,"RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %lu. body: 0x%02x", m_packetType, m_nChannel,
- m_nInfoField1, m_nInfoField2, m_nBodySize, m_body?(unsigned char)m_body[0]:0);
-}
+++ /dev/null
-#ifndef __RTMP_PACKET__
-#define __RTMP_PACKET__
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- * Copyright (C) 2008-2009 Andrej Stepanchuk
- * Copyright (C) 2009 Howard Chu
- *
- * 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 RTMPDump; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include <string>
-#include <stdint.h>
-
-#define RTMP_PACKET_TYPE_AUDIO 0x08
-#define RTMP_PACKET_TYPE_VIDEO 0x09
-#define RTMP_PACKET_TYPE_INFO 0x12
-
-#define RTMP_MAX_HEADER_SIZE 14
-
-typedef unsigned char BYTE;
-
-typedef struct
-{
- BYTE Type; // 0x03 RTMP, 0x06 RTMPE
-
-} HANDSHAKE_SIGNATURE;
-
-namespace RTMP_LIB
-{
- class RTMPPacket
- {
- public:
- RTMPPacket();
- virtual ~RTMPPacket();
-
- void Reset();
- bool AllocPacket(int nSize);
- void FreePacket();
- void FreePacketHeader();
-
- inline bool IsReady() { return m_nBytesRead == m_nBodySize; }
- void Dump();
-
- BYTE m_headerType;
- BYTE m_packetType;
- int m_nChannel;
- int32_t m_nInfoField1; // 3 first bytes
- int32_t m_nInfoField2; // last 4 bytes in a long header, absolute timestamp for long headers, relative timestamp for short headers
- bool m_hasAbsTimestamp; // timestamp absolute or relative?
- uint32_t m_nTimeStamp; // absolute timestamp
- uint32_t m_nBodySize;
- uint32_t m_nBytesRead;
- char *m_body;
- char *m_buffer;
- };
-};
-
-#endif