mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-06-20 07:12:21 +08:00
初始提交
This commit is contained in:
528
src/Rtmp/amf.cpp
Normal file
528
src/Rtmp/amf.cpp
Normal file
@@ -0,0 +1,528 @@
|
||||
#include "amf.h"
|
||||
#include "utils.h"
|
||||
#include <stdexcept>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "Util/logger.h"
|
||||
#include "Util/util.h"
|
||||
using namespace ZL::Util;
|
||||
|
||||
/////////////////////AMFValue/////////////////////////////
|
||||
inline void AMFValue::destroy() {
|
||||
switch (m_type) {
|
||||
case AMF_STRING:
|
||||
if (m_value.string) {
|
||||
delete m_value.string;
|
||||
m_value.string = nullptr;
|
||||
}
|
||||
break;
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
if (m_value.object) {
|
||||
delete m_value.object;
|
||||
m_value.object = nullptr;
|
||||
}
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
if (m_value.array) {
|
||||
delete m_value.array;
|
||||
m_value.array = nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void AMFValue::init() {
|
||||
switch (m_type) {
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
m_value.object = new mapType;
|
||||
break;
|
||||
case AMF_STRING:
|
||||
m_value.string = new std::string;
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
m_value.array = new arrayType;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
AMFValue::AMFValue(AMFType type) :
|
||||
m_type(type) {
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
AMFValue::~AMFValue() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(const char *s) :
|
||||
m_type(AMF_STRING) {
|
||||
init();
|
||||
*m_value.string = s;
|
||||
}
|
||||
|
||||
|
||||
AMFValue::AMFValue(const std::string &s) :
|
||||
m_type(AMF_STRING) {
|
||||
init();
|
||||
*m_value.string = s;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(double n) :
|
||||
m_type(AMF_NUMBER) {
|
||||
init();
|
||||
m_value.number = n;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(int i) :
|
||||
m_type(AMF_INTEGER) {
|
||||
init();
|
||||
m_value.integer = i;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(bool b) :
|
||||
m_type(AMF_BOOLEAN) {
|
||||
init();
|
||||
m_value.boolean = b;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(const AMFValue &from) :
|
||||
m_type(AMF_NULL) {
|
||||
*this = from;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(AMFValue &&from) {
|
||||
*this = from;
|
||||
}
|
||||
|
||||
AMFValue& AMFValue::operator =(const AMFValue &from) {
|
||||
return *this = const_cast<AMFValue &&>(from);
|
||||
|
||||
}
|
||||
AMFValue& AMFValue::operator =(AMFValue &&from) {
|
||||
destroy();
|
||||
m_type = from.m_type;
|
||||
init();
|
||||
switch (m_type) {
|
||||
case AMF_STRING:
|
||||
*m_value.string = (*from.m_value.string);
|
||||
break;
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
*m_value.object = (*from.m_value.object);
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
*m_value.array = (*from.m_value.array);
|
||||
break;
|
||||
case AMF_NUMBER:
|
||||
m_value.number = from.m_value.number;
|
||||
break;
|
||||
case AMF_INTEGER:
|
||||
m_value.integer = from.m_value.integer;
|
||||
break;
|
||||
case AMF_BOOLEAN:
|
||||
m_value.boolean = from.m_value.boolean;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum {
|
||||
AMF0_NUMBER,
|
||||
AMF0_BOOLEAN,
|
||||
AMF0_STRING,
|
||||
AMF0_OBJECT,
|
||||
AMF0_MOVIECLIP,
|
||||
AMF0_NULL,
|
||||
AMF0_UNDEFINED,
|
||||
AMF0_REFERENCE,
|
||||
AMF0_ECMA_ARRAY,
|
||||
AMF0_OBJECT_END,
|
||||
AMF0_STRICT_ARRAY,
|
||||
AMF0_DATE,
|
||||
AMF0_LONG_STRING,
|
||||
AMF0_UNSUPPORTED,
|
||||
AMF0_RECORD_SET,
|
||||
AMF0_XML_OBJECT,
|
||||
AMF0_TYPED_OBJECT,
|
||||
AMF0_SWITCH_AMF3,
|
||||
};
|
||||
|
||||
enum {
|
||||
AMF3_UNDEFINED,
|
||||
AMF3_NULL,
|
||||
AMF3_FALSE,
|
||||
AMF3_TRUE,
|
||||
AMF3_INTEGER,
|
||||
AMF3_NUMBER,
|
||||
AMF3_STRING,
|
||||
AMF3_LEGACY_XML,
|
||||
AMF3_DATE,
|
||||
AMF3_ARRAY,
|
||||
AMF3_OBJECT,
|
||||
AMF3_XML,
|
||||
AMF3_BYTE_ARRAY,
|
||||
};
|
||||
|
||||
////////////////////////////////Encoder//////////////////////////////////////////
|
||||
AMFEncoder & AMFEncoder::operator <<(const char *s) {
|
||||
if (s) {
|
||||
buf += char(AMF0_STRING);
|
||||
uint16_t str_len = htons(strlen(s));
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
} else {
|
||||
buf += char(AMF0_NULL);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(const std::string &s) {
|
||||
if (!s.empty()) {
|
||||
buf += char(AMF0_STRING);
|
||||
uint16_t str_len = htons(s.size());
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
} else {
|
||||
buf += char(AMF0_NULL);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(std::nullptr_t) {
|
||||
buf += char(AMF0_NULL);
|
||||
return *this;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::write_undefined() {
|
||||
buf += char(AMF0_UNDEFINED);
|
||||
return *this;
|
||||
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(const int n){
|
||||
return (*this) << (double)n;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(const double n) {
|
||||
buf += char(AMF0_NUMBER);
|
||||
uint64_t encoded = 0;
|
||||
memcpy(&encoded, &n, 8);
|
||||
uint32_t val = htonl(encoded >> 32);
|
||||
buf.append((char *) &val, 4);
|
||||
val = htonl(encoded);
|
||||
buf.append((char *) &val, 4);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AMFEncoder & AMFEncoder::operator <<(const bool b) {
|
||||
buf += char(AMF0_BOOLEAN);
|
||||
buf += char(b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AMFEncoder & AMFEncoder::operator <<(const AMFValue& value) {
|
||||
switch ((int) value.type()) {
|
||||
case AMF_STRING:
|
||||
*this << value.as_string();
|
||||
break;
|
||||
case AMF_NUMBER:
|
||||
*this << value.as_number();
|
||||
break;
|
||||
case AMF_INTEGER:
|
||||
*this << value.as_integer();
|
||||
break;
|
||||
case AMF_BOOLEAN:
|
||||
*this << value.as_boolean();
|
||||
break;
|
||||
case AMF_OBJECT: {
|
||||
buf += char(AMF0_OBJECT);
|
||||
for (auto &pr : value.getMap()) {
|
||||
write_key(pr.first);
|
||||
*this << pr.second;
|
||||
}
|
||||
write_key("");
|
||||
buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
case AMF_ECMA_ARRAY: {
|
||||
buf += char(AMF0_ECMA_ARRAY);
|
||||
uint32_t sz = htonl(value.getMap().size());
|
||||
buf.append((char *) &sz, 4);
|
||||
for (auto &pr : value.getMap()) {
|
||||
write_key(pr.first);
|
||||
*this << pr.second;
|
||||
}
|
||||
write_key("");
|
||||
buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
case AMF_NULL:
|
||||
*this << nullptr;
|
||||
break;
|
||||
case AMF_UNDEFINED:
|
||||
this->write_undefined();
|
||||
break;
|
||||
case AMF_STRICT_ARRAY: {
|
||||
buf += char(AMF0_STRICT_ARRAY);
|
||||
uint32_t sz = htonl(value.getArr().size());
|
||||
buf.append((char *) &sz, 4);
|
||||
for (auto &val : value.getArr()) {
|
||||
*this << val;
|
||||
}
|
||||
//write_key("");
|
||||
//buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
void AMFEncoder::write_key(const std::string& s) {
|
||||
uint16_t str_len = htons(s.size());
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
}
|
||||
|
||||
//////////////////Decoder//////////////////
|
||||
|
||||
uint8_t AMFDecoder::front() {
|
||||
if (pos >= buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
return uint8_t(buf[pos]);
|
||||
}
|
||||
|
||||
uint8_t AMFDecoder::pop_front() {
|
||||
if (version == 0 && front() == AMF0_SWITCH_AMF3) {
|
||||
InfoL << "entering AMF3 mode";
|
||||
pos++;
|
||||
version = 3;
|
||||
}
|
||||
|
||||
if (pos >= buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
return uint8_t(buf[pos++]);
|
||||
}
|
||||
|
||||
template<>
|
||||
double AMFDecoder::load<double>() {
|
||||
if (pop_front() != AMF0_NUMBER) {
|
||||
throw std::runtime_error("Expected a number");
|
||||
}
|
||||
if (pos + 8 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
uint64_t val = ((uint64_t) load_be32(&buf[pos]) << 32)
|
||||
| load_be32(&buf[pos + 4]);
|
||||
double n = 0;
|
||||
memcpy(&n, &val, 8);
|
||||
pos += 8;
|
||||
return n;
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
bool AMFDecoder::load<bool>() {
|
||||
if (pop_front() != AMF0_BOOLEAN) {
|
||||
throw std::runtime_error("Expected a boolean");
|
||||
}
|
||||
return pop_front() != 0;
|
||||
}
|
||||
template<>
|
||||
unsigned int AMFDecoder::load<unsigned int>() {
|
||||
unsigned int value = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint8_t b = pop_front();
|
||||
if (i == 3) {
|
||||
/* use all bits from 4th byte */
|
||||
value = (value << 8) | b;
|
||||
break;
|
||||
}
|
||||
value = (value << 7) | (b & 0x7f);
|
||||
if ((b & 0x80) == 0)
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template<>
|
||||
int AMFDecoder::load<int>() {
|
||||
if (version == 3) {
|
||||
return load<unsigned int>();
|
||||
} else {
|
||||
return load<double>();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string AMFDecoder::load<std::string>() {
|
||||
size_t str_len = 0;
|
||||
uint8_t type = pop_front();
|
||||
if (version == 3) {
|
||||
if (type != AMF3_STRING) {
|
||||
throw std::runtime_error("Expected a string");
|
||||
}
|
||||
str_len = load<unsigned int>() / 2;
|
||||
|
||||
} else {
|
||||
if (type != AMF0_STRING) {
|
||||
throw std::runtime_error("Expected a string");
|
||||
}
|
||||
if (pos + 2 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
str_len = load_be16(&buf[pos]);
|
||||
pos += 2;
|
||||
}
|
||||
if (pos + str_len > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
std::string s(buf, pos, str_len);
|
||||
pos += str_len;
|
||||
return s;
|
||||
}
|
||||
|
||||
template<>
|
||||
AMFValue AMFDecoder::load<AMFValue>() {
|
||||
uint8_t type = front();
|
||||
if (version == 3) {
|
||||
switch (type) {
|
||||
case AMF3_STRING:
|
||||
return load<std::string>();
|
||||
case AMF3_NUMBER:
|
||||
return load<double>();
|
||||
case AMF3_INTEGER:
|
||||
return load<int>();
|
||||
case AMF3_FALSE:
|
||||
pos++;
|
||||
return false;
|
||||
case AMF3_TRUE:
|
||||
pos++;
|
||||
return true;
|
||||
case AMF3_OBJECT:
|
||||
return load_object();
|
||||
case AMF3_ARRAY:
|
||||
return load_ecma();
|
||||
case AMF3_NULL:
|
||||
pos++;
|
||||
return AMF_NULL;
|
||||
case AMF3_UNDEFINED:
|
||||
pos++;
|
||||
return AMF_UNDEFINED;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "Unsupported AMF3 type:" << (int) type << endl);
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case AMF0_STRING:
|
||||
return load<std::string>();
|
||||
case AMF0_NUMBER:
|
||||
return load<double>();
|
||||
case AMF0_BOOLEAN:
|
||||
return load<bool>();
|
||||
case AMF0_OBJECT:
|
||||
return load_object();
|
||||
case AMF0_ECMA_ARRAY:
|
||||
return load_ecma();
|
||||
case AMF0_NULL:
|
||||
pos++;
|
||||
return AMF_NULL;
|
||||
case AMF0_UNDEFINED:
|
||||
pos++;
|
||||
return AMF_UNDEFINED;
|
||||
case AMF0_STRICT_ARRAY:
|
||||
return load_arr();
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "Unsupported AMF type:" << (int) type << endl);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string AMFDecoder::load_key() {
|
||||
if (pos + 2 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
size_t str_len = load_be16(&buf[pos]);
|
||||
pos += 2;
|
||||
if (pos + str_len > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
std::string s(buf, pos, str_len);
|
||||
pos += str_len;
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
AMFValue AMFDecoder::load_object() {
|
||||
AMFValue object(AMF_OBJECT);
|
||||
if (pop_front() != AMF0_OBJECT) {
|
||||
throw std::runtime_error("Expected an object");
|
||||
}
|
||||
while (1) {
|
||||
std::string key = load_key();
|
||||
if (key.empty())
|
||||
break;
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.set(key, value);
|
||||
}
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
AMFValue AMFDecoder::load_ecma() {
|
||||
/* ECMA array is the same as object, with 4 extra zero bytes */
|
||||
AMFValue object(AMF_ECMA_ARRAY);
|
||||
if (pop_front() != AMF0_ECMA_ARRAY) {
|
||||
throw std::runtime_error("Expected an ECMA array");
|
||||
}
|
||||
if (pos + 4 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
pos += 4;
|
||||
while (1) {
|
||||
std::string key = load_key();
|
||||
if (key.empty())
|
||||
break;
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.set(key, value);
|
||||
}
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
AMFValue AMFDecoder::load_arr() {
|
||||
/* ECMA array is the same as object, with 4 extra zero bytes */
|
||||
AMFValue object(AMF_STRICT_ARRAY);
|
||||
if (pop_front() != AMF0_STRICT_ARRAY) {
|
||||
throw std::runtime_error("Expected an STRICT array");
|
||||
}
|
||||
if (pos + 4 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
int arrSize = load_be32(&buf[pos]);
|
||||
pos += 4;
|
||||
while (arrSize--) {
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.add(value);
|
||||
}
|
||||
/*pos += 2;
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}*/
|
||||
return object;
|
||||
}
|
||||
Reference in New Issue
Block a user