You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
649 lines
16 KiB
649 lines
16 KiB
5 years ago
|
|
||
|
/*
|
||
|
* Copyright (C) Roman Arutyunyan
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include <ngx_config.h>
|
||
|
#include <ngx_core.h>
|
||
|
#include "ngx_rtmp_amf.h"
|
||
|
#include "ngx_rtmp.h"
|
||
|
#include <string.h>
|
||
|
|
||
|
|
||
|
static ngx_inline void*
|
||
|
ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len)
|
||
|
{
|
||
|
size_t k;
|
||
|
|
||
|
if (dst == NULL || src == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for(k = 0; k < len; ++k) {
|
||
|
((u_char*)dst)[k] = ((u_char*)src)[len - 1 - k];
|
||
|
}
|
||
|
|
||
|
return dst;
|
||
|
}
|
||
|
|
||
|
#define NGX_RTMP_AMF_DEBUG_SIZE 16
|
||
|
|
||
|
#ifdef NGX_DEBUG
|
||
|
static void
|
||
|
ngx_rtmp_amf_debug(const char* op, ngx_log_t *log, u_char *p, size_t n)
|
||
|
{
|
||
|
u_char hstr[3 * NGX_RTMP_AMF_DEBUG_SIZE + 1];
|
||
|
u_char str[NGX_RTMP_AMF_DEBUG_SIZE + 1];
|
||
|
u_char *hp, *sp;
|
||
|
static u_char hex[] = "0123456789ABCDEF";
|
||
|
size_t i;
|
||
|
|
||
|
hp = hstr;
|
||
|
sp = str;
|
||
|
|
||
|
for(i = 0; i < n && i < NGX_RTMP_AMF_DEBUG_SIZE; ++i) {
|
||
|
*hp++ = ' ';
|
||
|
if (p) {
|
||
|
*hp++ = hex[(*p & 0xf0) >> 4];
|
||
|
*hp++ = hex[*p & 0x0f];
|
||
|
*sp++ = (*p >= 0x20 && *p <= 0x7e) ?
|
||
|
*p : (u_char)'?';
|
||
|
++p;
|
||
|
} else {
|
||
|
*hp++ = 'X';
|
||
|
*hp++ = 'X';
|
||
|
*sp++ = '?';
|
||
|
}
|
||
|
}
|
||
|
*hp = *sp = '\0';
|
||
|
|
||
|
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, log, 0,
|
||
|
"AMF %s (%d)%s '%s'", op, n, hstr, str);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_get(ngx_rtmp_amf_ctx_t *ctx, void *p, size_t n)
|
||
|
{
|
||
|
size_t size;
|
||
|
ngx_chain_t *l;
|
||
|
size_t offset;
|
||
|
u_char *pos, *last;
|
||
|
#ifdef NGX_DEBUG
|
||
|
void *op = p;
|
||
|
size_t on = n;
|
||
|
#endif
|
||
|
|
||
|
if (!n)
|
||
|
return NGX_OK;
|
||
|
|
||
|
for(l = ctx->link, offset = ctx->offset; l; l = l->next, offset = 0) {
|
||
|
|
||
|
pos = l->buf->pos + offset;
|
||
|
last = l->buf->last;
|
||
|
|
||
|
if (last >= pos + n) {
|
||
|
if (p) {
|
||
|
p = ngx_cpymem(p, pos, n);
|
||
|
}
|
||
|
ctx->offset = offset + n;
|
||
|
ctx->link = l;
|
||
|
|
||
|
#ifdef NGX_DEBUG
|
||
|
ngx_rtmp_amf_debug("read", ctx->log, (u_char*)op, on);
|
||
|
#endif
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
size = last - pos;
|
||
|
|
||
|
if (p) {
|
||
|
p = ngx_cpymem(p, pos, size);
|
||
|
}
|
||
|
|
||
|
n -= size;
|
||
|
}
|
||
|
|
||
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ctx->log, 0,
|
||
|
"AMF read eof (%d)", n);
|
||
|
|
||
|
return NGX_DONE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_put(ngx_rtmp_amf_ctx_t *ctx, void *p, size_t n)
|
||
|
{
|
||
|
ngx_buf_t *b;
|
||
|
size_t size;
|
||
|
ngx_chain_t *l, *ln;
|
||
|
|
||
|
#ifdef NGX_DEBUG
|
||
|
ngx_rtmp_amf_debug("write", ctx->log, (u_char*)p, n);
|
||
|
#endif
|
||
|
|
||
|
l = ctx->link;
|
||
|
|
||
|
if (ctx->link && ctx->first == NULL) {
|
||
|
ctx->first = ctx->link;
|
||
|
}
|
||
|
|
||
|
while(n) {
|
||
|
b = l ? l->buf : NULL;
|
||
|
|
||
|
if (b == NULL || b->last == b->end) {
|
||
|
|
||
|
ln = ctx->alloc(ctx->arg);
|
||
|
if (ln == NULL) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
if (ctx->first == NULL) {
|
||
|
ctx->first = ln;
|
||
|
}
|
||
|
|
||
|
if (l) {
|
||
|
l->next = ln;
|
||
|
}
|
||
|
|
||
|
l = ln;
|
||
|
ctx->link = l;
|
||
|
b = l->buf;
|
||
|
}
|
||
|
|
||
|
size = b->end - b->last;
|
||
|
|
||
|
if (size >= n) {
|
||
|
b->last = ngx_cpymem(b->last, p, n);
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
b->last = ngx_cpymem(b->last, p, size);
|
||
|
p = (u_char*)p + size;
|
||
|
n -= size;
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||
|
size_t nelts)
|
||
|
{
|
||
|
uint8_t type;
|
||
|
uint16_t len;
|
||
|
size_t n, namelen, maxlen;
|
||
|
ngx_int_t rc;
|
||
|
u_char buf[2];
|
||
|
|
||
|
maxlen = 0;
|
||
|
for(n = 0; n < nelts; ++n) {
|
||
|
namelen = elts[n].name.len;
|
||
|
if (namelen > maxlen)
|
||
|
maxlen = namelen;
|
||
|
}
|
||
|
|
||
|
for( ;; ) {
|
||
|
|
||
|
#if !(NGX_WIN32)
|
||
|
char name[maxlen];
|
||
|
#else
|
||
|
char name[1024];
|
||
|
if (maxlen > sizeof(name)) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
#endif
|
||
|
/* read key */
|
||
|
switch (ngx_rtmp_amf_get(ctx, buf, 2)) {
|
||
|
case NGX_DONE:
|
||
|
/* Envivio sends unfinalized arrays */
|
||
|
return NGX_OK;
|
||
|
case NGX_OK:
|
||
|
break;
|
||
|
default:
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
ngx_rtmp_amf_reverse_copy(&len, buf, 2);
|
||
|
|
||
|
if (!len)
|
||
|
break;
|
||
|
|
||
|
if (len <= maxlen) {
|
||
|
rc = ngx_rtmp_amf_get(ctx, name, len);
|
||
|
|
||
|
} else {
|
||
|
rc = ngx_rtmp_amf_get(ctx, name, maxlen);
|
||
|
if (rc != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
rc = ngx_rtmp_amf_get(ctx, 0, len - maxlen);
|
||
|
}
|
||
|
|
||
|
if (rc != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
|
||
|
/* TODO: if we require array to be sorted on name
|
||
|
* then we could be able to use binary search */
|
||
|
for(n = 0; n < nelts
|
||
|
&& (len != elts[n].name.len
|
||
|
|| ngx_strncmp(name, elts[n].name.data, len));
|
||
|
++n);
|
||
|
|
||
|
if (ngx_rtmp_amf_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
if (ngx_rtmp_amf_get(ctx, &type, 1) != NGX_OK
|
||
|
|| type != NGX_RTMP_AMF_END)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||
|
size_t nelts)
|
||
|
{
|
||
|
uint32_t len;
|
||
|
size_t n;
|
||
|
u_char buf[4];
|
||
|
|
||
|
/* read length */
|
||
|
if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
|
||
|
ngx_rtmp_amf_reverse_copy(&len, buf, 4);
|
||
|
|
||
|
for (n = 0; n < len; ++n) {
|
||
|
if (ngx_rtmp_amf_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_read_variant(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||
|
size_t nelts)
|
||
|
{
|
||
|
uint8_t type;
|
||
|
ngx_int_t rc;
|
||
|
size_t n;
|
||
|
ngx_rtmp_amf_elt_t elt;
|
||
|
|
||
|
rc = ngx_rtmp_amf_get(ctx, &type, 1);
|
||
|
if (rc != NGX_OK) {
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
ngx_memzero(&elt, sizeof(elt));
|
||
|
for (n = 0; n < nelts; ++n, ++elts) {
|
||
|
if (type == elts->type) {
|
||
|
elt.data = elts->data;
|
||
|
elt.len = elts->len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
elt.type = type | NGX_RTMP_AMF_TYPELESS;
|
||
|
|
||
|
return ngx_rtmp_amf_read(ctx, &elt, 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_is_compatible_type(uint8_t t1, uint8_t t2)
|
||
|
{
|
||
|
return t1 == t2
|
||
|
|| (t1 == NGX_RTMP_AMF_OBJECT && t2 == NGX_RTMP_AMF_MIXED_ARRAY)
|
||
|
|| (t2 == NGX_RTMP_AMF_OBJECT && t1 == NGX_RTMP_AMF_MIXED_ARRAY);
|
||
|
}
|
||
|
|
||
|
|
||
|
ngx_int_t
|
||
|
ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
||
|
size_t nelts)
|
||
|
{
|
||
|
void *data;
|
||
|
ngx_int_t type;
|
||
|
uint8_t type8;
|
||
|
size_t n;
|
||
|
uint16_t len;
|
||
|
ngx_int_t rc;
|
||
|
u_char buf[8];
|
||
|
uint32_t max_index;
|
||
|
|
||
|
for(n = 0; n < nelts; ++n) {
|
||
|
|
||
|
if (elts && elts->type & NGX_RTMP_AMF_TYPELESS) {
|
||
|
type = elts->type & ~NGX_RTMP_AMF_TYPELESS;
|
||
|
data = elts->data;
|
||
|
|
||
|
} else {
|
||
|
switch (ngx_rtmp_amf_get(ctx, &type8, 1)) {
|
||
|
case NGX_DONE:
|
||
|
if (elts->type & NGX_RTMP_AMF_OPTIONAL) {
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
/* fall through */
|
||
|
case NGX_ERROR:
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
type = type8;
|
||
|
data = (elts &&
|
||
|
ngx_rtmp_amf_is_compatible_type(
|
||
|
(uint8_t) (elts->type & 0xff), (uint8_t) type))
|
||
|
? elts->data
|
||
|
: NULL;
|
||
|
|
||
|
if (elts && (elts->type & NGX_RTMP_AMF_CONTEXT)) {
|
||
|
if (data) {
|
||
|
*(ngx_rtmp_amf_ctx_t *) data = *ctx;
|
||
|
}
|
||
|
data = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (type) {
|
||
|
case NGX_RTMP_AMF_NUMBER:
|
||
|
if (ngx_rtmp_amf_get(ctx, buf, 8) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
ngx_rtmp_amf_reverse_copy(data, buf, 8);
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_BOOLEAN:
|
||
|
if (ngx_rtmp_amf_get(ctx, data, 1) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_STRING:
|
||
|
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
ngx_rtmp_amf_reverse_copy(&len, buf, 2);
|
||
|
|
||
|
if (data == NULL) {
|
||
|
rc = ngx_rtmp_amf_get(ctx, data, len);
|
||
|
|
||
|
} else if (elts->len <= len) {
|
||
|
rc = ngx_rtmp_amf_get(ctx, data, elts->len - 1);
|
||
|
if (rc != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
((char*)data)[elts->len - 1] = 0;
|
||
|
rc = ngx_rtmp_amf_get(ctx, NULL, len - elts->len + 1);
|
||
|
|
||
|
} else {
|
||
|
rc = ngx_rtmp_amf_get(ctx, data, len);
|
||
|
((char*)data)[len] = 0;
|
||
|
}
|
||
|
|
||
|
if (rc != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_NULL:
|
||
|
case NGX_RTMP_AMF_ARRAY_NULL:
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_MIXED_ARRAY:
|
||
|
if (ngx_rtmp_amf_get(ctx, &max_index, 4) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
/* fall through */
|
||
|
|
||
|
case NGX_RTMP_AMF_OBJECT:
|
||
|
if (ngx_rtmp_amf_read_object(ctx, data,
|
||
|
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
|
||
|
) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_ARRAY:
|
||
|
if (ngx_rtmp_amf_read_array(ctx, data,
|
||
|
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
|
||
|
) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_VARIANT_:
|
||
|
if (ngx_rtmp_amf_read_variant(ctx, data,
|
||
|
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
|
||
|
) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_INT8:
|
||
|
if (ngx_rtmp_amf_get(ctx, data, 1) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_INT16:
|
||
|
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
ngx_rtmp_amf_reverse_copy(data, buf, 2);
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_INT32:
|
||
|
if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
ngx_rtmp_amf_reverse_copy(data, buf, 4);
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_END:
|
||
|
return NGX_OK;
|
||
|
|
||
|
default:
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
if (elts) {
|
||
|
++elts;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
|
||
|
ngx_rtmp_amf_elt_t *elts, size_t nelts)
|
||
|
{
|
||
|
uint16_t len;
|
||
|
size_t n;
|
||
|
u_char buf[2];
|
||
|
|
||
|
for(n = 0; n < nelts; ++n) {
|
||
|
|
||
|
len = (uint16_t) elts[n].name.len;
|
||
|
|
||
|
if (ngx_rtmp_amf_put(ctx,
|
||
|
ngx_rtmp_amf_reverse_copy(buf,
|
||
|
&len, 2), 2) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
if (ngx_rtmp_amf_put(ctx, elts[n].name.data, len) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
if (ngx_rtmp_amf_write(ctx, &elts[n], 1) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ngx_rtmp_amf_put(ctx, "\0\0", 2) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ngx_int_t
|
||
|
ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx,
|
||
|
ngx_rtmp_amf_elt_t *elts, size_t nelts)
|
||
|
{
|
||
|
uint32_t len;
|
||
|
size_t n;
|
||
|
u_char buf[4];
|
||
|
|
||
|
len = nelts;
|
||
|
if (ngx_rtmp_amf_put(ctx,
|
||
|
ngx_rtmp_amf_reverse_copy(buf,
|
||
|
&len, 4), 4) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
for(n = 0; n < nelts; ++n) {
|
||
|
if (ngx_rtmp_amf_write(ctx, &elts[n], 1) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
ngx_int_t
|
||
|
ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
|
||
|
ngx_rtmp_amf_elt_t *elts, size_t nelts)
|
||
|
{
|
||
|
size_t n;
|
||
|
ngx_int_t type;
|
||
|
uint8_t type8;
|
||
|
void *data;
|
||
|
uint16_t len;
|
||
|
uint32_t max_index;
|
||
|
u_char buf[8];
|
||
|
|
||
|
for(n = 0; n < nelts; ++n) {
|
||
|
|
||
|
type = elts[n].type;
|
||
|
data = elts[n].data;
|
||
|
len = (uint16_t) elts[n].len;
|
||
|
|
||
|
if (type & NGX_RTMP_AMF_TYPELESS) {
|
||
|
type &= ~NGX_RTMP_AMF_TYPELESS;
|
||
|
} else {
|
||
|
type8 = (uint8_t)type;
|
||
|
if (ngx_rtmp_amf_put(ctx, &type8, 1) != NGX_OK)
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
switch(type) {
|
||
|
case NGX_RTMP_AMF_NUMBER:
|
||
|
if (ngx_rtmp_amf_put(ctx,
|
||
|
ngx_rtmp_amf_reverse_copy(buf,
|
||
|
data, 8), 8) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_BOOLEAN:
|
||
|
if (ngx_rtmp_amf_put(ctx, data, 1) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_STRING:
|
||
|
if (len == 0 && data) {
|
||
|
len = (uint16_t) ngx_strlen((u_char*) data);
|
||
|
}
|
||
|
|
||
|
if (ngx_rtmp_amf_put(ctx,
|
||
|
ngx_rtmp_amf_reverse_copy(buf,
|
||
|
&len, 2), 2) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
|
||
|
if (ngx_rtmp_amf_put(ctx, data, len) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_NULL:
|
||
|
case NGX_RTMP_AMF_ARRAY_NULL:
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_MIXED_ARRAY:
|
||
|
max_index = 0;
|
||
|
if (ngx_rtmp_amf_put(ctx, &max_index, 4) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
/* fall through */
|
||
|
|
||
|
case NGX_RTMP_AMF_OBJECT:
|
||
|
type8 = NGX_RTMP_AMF_END;
|
||
|
if (ngx_rtmp_amf_write_object(ctx, data,
|
||
|
elts[n].len / sizeof(ngx_rtmp_amf_elt_t)) != NGX_OK
|
||
|
|| ngx_rtmp_amf_put(ctx, &type8, 1) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_ARRAY:
|
||
|
if (ngx_rtmp_amf_write_array(ctx, data,
|
||
|
elts[n].len / sizeof(ngx_rtmp_amf_elt_t)) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_INT8:
|
||
|
if (ngx_rtmp_amf_put(ctx, data, 1) != NGX_OK) {
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_INT16:
|
||
|
if (ngx_rtmp_amf_put(ctx,
|
||
|
ngx_rtmp_amf_reverse_copy(buf,
|
||
|
data, 2), 2) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NGX_RTMP_AMF_INT32:
|
||
|
if (ngx_rtmp_amf_put(ctx,
|
||
|
ngx_rtmp_amf_reverse_copy(buf,
|
||
|
data, 4), 4) != NGX_OK)
|
||
|
{
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return NGX_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NGX_OK;
|
||
|
}
|
||
|
|