re/src/rtmp/hdr.c
2018-10-24 11:18:52 +02:00

256 lines
4.8 KiB
C

/**
* @file rtmp/hdr.c Real Time Messaging Protocol (RTMP) -- Headers
*
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_net.h>
#include <re_sa.h>
#include <re_list.h>
#include <re_sys.h>
#include <re_rtmp.h>
#include "rtmp.h"
enum {
RTMP_CHUNK_ID_MIN = 3,
RTMP_CHUNK_ID_MAX = 65599, /* 65535 + 64 */
RTMP_CHUNK_OFFSET = 64,
};
static int mbuf_write_u24_hton(struct mbuf *mb, uint32_t u24)
{
int err = 0;
err |= mbuf_write_u8(mb, u24 >> 16);
err |= mbuf_write_u8(mb, u24 >> 8);
err |= mbuf_write_u8(mb, u24 >> 0);
return err;
}
static uint32_t mbuf_read_u24_ntoh(struct mbuf *mb)
{
uint32_t u24;
u24 = (uint32_t)mbuf_read_u8(mb) << 16;
u24 |= (uint32_t)mbuf_read_u8(mb) << 8;
u24 |= (uint32_t)mbuf_read_u8(mb) << 0;
return u24;
}
static int encode_basic_hdr(struct mbuf *mb, unsigned fmt,
uint32_t chunk_id)
{
uint8_t v;
int err = 0;
if (chunk_id >= 320) {
const uint16_t cs_id = chunk_id - RTMP_CHUNK_OFFSET;
v = fmt<<6 | 1;
err |= mbuf_write_u8(mb, v);
err |= mbuf_write_u16(mb, htons(cs_id));
}
else if (chunk_id >= RTMP_CHUNK_OFFSET) {
const uint8_t cs_id = chunk_id - RTMP_CHUNK_OFFSET;
v = fmt<<6 | 0;
err |= mbuf_write_u8(mb, v);
err |= mbuf_write_u8(mb, cs_id);
}
else {
v = fmt<<6 | chunk_id;
err |= mbuf_write_u8(mb, v);
}
return err;
}
static int decode_basic_hdr(struct rtmp_header *hdr, struct mbuf *mb)
{
uint8_t cs_id;
uint8_t v;
if (mbuf_get_left(mb) < 1)
return ENODATA;
v = mbuf_read_u8(mb);
hdr->format = v>>6;
cs_id = v & 0x3f;
switch (cs_id) {
case 0:
if (mbuf_get_left(mb) < 1)
return ENODATA;
hdr->chunk_id = mbuf_read_u8(mb) + RTMP_CHUNK_OFFSET;
break;
case 1:
if (mbuf_get_left(mb) < 2)
return ENODATA;
hdr->chunk_id = ntohs(mbuf_read_u16(mb)) + RTMP_CHUNK_OFFSET;
break;
default:
hdr->chunk_id = cs_id;
break;
}
return 0;
}
int rtmp_header_encode(struct mbuf *mb, const struct rtmp_header *hdr)
{
bool ext_ts;
int err = 0;
if (!mb || !hdr)
return EINVAL;
err = encode_basic_hdr(mb, hdr->format, hdr->chunk_id);
if (err)
return err;
switch (hdr->format) {
case 0:
ext_ts = (hdr->timestamp >= 0x00ffffff);
err |= mbuf_write_u24_hton(mb,
ext_ts ? 0xffffff : hdr->timestamp);
err |= mbuf_write_u24_hton(mb, hdr->length);
err |= mbuf_write_u8(mb, hdr->type_id);
err |= mbuf_write_u32(mb, sys_htoll(hdr->stream_id));
if (ext_ts) {
err |= mbuf_write_u32(mb, htonl(hdr->timestamp));
}
break;
case 1:
err |= mbuf_write_u24_hton(mb, hdr->timestamp_delta);
err |= mbuf_write_u24_hton(mb, hdr->length);
err |= mbuf_write_u8(mb, hdr->type_id);
break;
case 2:
err |= mbuf_write_u24_hton(mb, hdr->timestamp_delta);
break;
case 3:
break;
}
return err;
}
int rtmp_header_decode(struct rtmp_header *hdr, struct mbuf *mb)
{
int err;
if (!hdr || !mb)
return EINVAL;
memset(hdr, 0, sizeof(*hdr));
err = decode_basic_hdr(hdr, mb);
if (err)
return err;
switch (hdr->format) {
case 0:
if (mbuf_get_left(mb) < 11)
return ENODATA;
hdr->timestamp = mbuf_read_u24_ntoh(mb);
hdr->length = mbuf_read_u24_ntoh(mb);
hdr->type_id = mbuf_read_u8(mb);
hdr->stream_id = sys_ltohl(mbuf_read_u32(mb));
if (hdr->timestamp == 0x00ffffff) {
if (mbuf_get_left(mb) < 4)
return ENODATA;
hdr->timestamp = ntohl(mbuf_read_u32(mb));
}
break;
case 1:
if (mbuf_get_left(mb) < 7)
return ENODATA;
hdr->timestamp_delta = mbuf_read_u24_ntoh(mb);
hdr->length = mbuf_read_u24_ntoh(mb);
hdr->type_id = mbuf_read_u8(mb);
break;
case 2:
if (mbuf_get_left(mb) < 3)
return ENODATA;
hdr->timestamp_delta = mbuf_read_u24_ntoh(mb);
break;
case 3:
/* no payload */
break;
}
return 0;
}
int rtmp_header_print(struct re_printf *pf, const struct rtmp_header *hdr)
{
if (!hdr)
return 0;
return re_hprintf(pf,
"fmt %u, chunk %u, "
"timestamp %5u, ts_delta %2u,"
" len %3u, type %2u (%-14s) stream_id %u",
hdr->format, hdr->chunk_id, hdr->timestamp,
hdr->timestamp_delta, hdr->length, hdr->type_id,
rtmp_packet_type_name(hdr->type_id), hdr->stream_id);
}
const char *rtmp_packet_type_name(enum rtmp_packet_type type)
{
switch (type) {
case RTMP_TYPE_SET_CHUNK_SIZE: return "Set Chunk Size";
case RTMP_TYPE_ACKNOWLEDGEMENT: return "Acknowledgement";
case RTMP_TYPE_USER_CONTROL_MSG: return "User Control Message";
case RTMP_TYPE_WINDOW_ACK_SIZE: return "Window Acknowledgement Size";
case RTMP_TYPE_SET_PEER_BANDWIDTH:return "Set Peer Bandwidth";
case RTMP_TYPE_AUDIO: return "Audio Message";
case RTMP_TYPE_VIDEO: return "Video Message";
case RTMP_TYPE_DATA: return "Data Message";
case RTMP_TYPE_AMF0: return "AMF";
default: return "?";
}
}