blob: 38bd3705a10b8dbb267dd7ef48aa0bae2079e9c6 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file rtmp/hdr.c Real Time Messaging Protocol (RTMP) -- Headers
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_net.h>
12#include <re_sa.h>
13#include <re_list.h>
14#include <re_sys.h>
15#include <re_rtmp.h>
16#include "rtmp.h"
17
18
19enum {
20 RTMP_CHUNK_ID_MIN = 3,
21 RTMP_CHUNK_ID_MAX = 65599, /* 65535 + 64 */
22
23 RTMP_CHUNK_OFFSET = 64,
24 TIMESTAMP_24MAX = 0x00ffffff,
25};
26
27
28static int mbuf_write_u24_hton(struct mbuf *mb, uint32_t u24)
29{
30 int err = 0;
31
32 err |= mbuf_write_u8(mb, u24 >> 16);
33 err |= mbuf_write_u8(mb, u24 >> 8);
34 err |= mbuf_write_u8(mb, u24 >> 0);
35
36 return err;
37}
38
39
40static uint32_t mbuf_read_u24_ntoh(struct mbuf *mb)
41{
42 uint32_t u24;
43
44 u24 = (uint32_t)mbuf_read_u8(mb) << 16;
45 u24 |= (uint32_t)mbuf_read_u8(mb) << 8;
46 u24 |= (uint32_t)mbuf_read_u8(mb) << 0;
47
48 return u24;
49}
50
51
52static int encode_basic_hdr(struct mbuf *mb, unsigned fmt,
53 uint32_t chunk_id)
54{
55 uint8_t v;
56 int err = 0;
57
58 if (chunk_id >= 320) {
59
60 const uint16_t cs_id = chunk_id - RTMP_CHUNK_OFFSET;
61
62 v = fmt<<6 | 1;
63
64 err |= mbuf_write_u8(mb, v);
65 err |= mbuf_write_u16(mb, htons(cs_id));
66 }
67 else if (chunk_id >= RTMP_CHUNK_OFFSET) {
68
69 const uint8_t cs_id = chunk_id - RTMP_CHUNK_OFFSET;
70
71 v = fmt<<6 | 0;
72
73 err |= mbuf_write_u8(mb, v);
74 err |= mbuf_write_u8(mb, cs_id);
75 }
76 else {
77 v = fmt<<6 | chunk_id;
78
79 err |= mbuf_write_u8(mb, v);
80 }
81
82 return err;
83}
84
85
86static int decode_basic_hdr(struct rtmp_header *hdr, struct mbuf *mb)
87{
88 uint8_t cs_id;
89 uint8_t v;
90
91 if (mbuf_get_left(mb) < 1)
92 return ENODATA;
93
94 v = mbuf_read_u8(mb);
95
96 hdr->format = v>>6;
97
98 cs_id = v & 0x3f;
99
100 switch (cs_id) {
101
102 case 0:
103 if (mbuf_get_left(mb) < 1)
104 return ENODATA;
105
106 hdr->chunk_id = mbuf_read_u8(mb) + RTMP_CHUNK_OFFSET;
107 break;
108
109 case 1:
110 if (mbuf_get_left(mb) < 2)
111 return ENODATA;
112
113 hdr->chunk_id = ntohs(mbuf_read_u16(mb)) + RTMP_CHUNK_OFFSET;
114 break;
115
116 default:
117 hdr->chunk_id = cs_id;
118 break;
119 }
120
121 return 0;
122}
123
124
125static uint32_t ts_24(uint32_t ts)
126{
127 return ts >= TIMESTAMP_24MAX ? TIMESTAMP_24MAX : ts;
128}
129
130
131static uint32_t ts_ext(uint32_t ts)
132{
133 return ts >= TIMESTAMP_24MAX ? ts : 0;
134}
135
136
137int rtmp_header_encode(struct mbuf *mb, struct rtmp_header *hdr)
138{
139 int err = 0;
140
141 if (!mb || !hdr)
142 return EINVAL;
143
144 err = encode_basic_hdr(mb, hdr->format, hdr->chunk_id);
145 if (err)
146 return err;
147
148 switch (hdr->format) {
149
150 case 0:
151 hdr->timestamp_ext = ts_ext(hdr->timestamp);
152
153 err |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp));
154 err |= mbuf_write_u24_hton(mb, hdr->length);
155 err |= mbuf_write_u8(mb, hdr->type_id);
156 err |= mbuf_write_u32(mb, sys_htoll(hdr->stream_id));
157 break;
158
159 case 1:
160 hdr->timestamp_ext = ts_ext(hdr->timestamp_delta);
161
162 err |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp_delta));
163 err |= mbuf_write_u24_hton(mb, hdr->length);
164 err |= mbuf_write_u8(mb, hdr->type_id);
165 break;
166
167 case 2:
168 hdr->timestamp_ext = ts_ext(hdr->timestamp_delta);
169
170 err |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp_delta));
171 break;
172
173 case 3:
174 break;
175 }
176
177 if (hdr->timestamp_ext) {
178 err |= mbuf_write_u32(mb, htonl(hdr->timestamp_ext));
179 }
180
181 return err;
182}
183
184
185int rtmp_header_decode(struct rtmp_header *hdr, struct mbuf *mb)
186{
187 uint32_t *timestamp_ext = NULL;
188 int err;
189
190 if (!hdr || !mb)
191 return EINVAL;
192
193 memset(hdr, 0, sizeof(*hdr));
194
195 err = decode_basic_hdr(hdr, mb);
196 if (err)
197 return err;
198
199 switch (hdr->format) {
200
201 case 0:
202 if (mbuf_get_left(mb) < 11)
203 return ENODATA;
204
205 hdr->timestamp = mbuf_read_u24_ntoh(mb);
206 hdr->length = mbuf_read_u24_ntoh(mb);
207 hdr->type_id = mbuf_read_u8(mb);
208 hdr->stream_id = sys_ltohl(mbuf_read_u32(mb));
209 break;
210
211 case 1:
212 if (mbuf_get_left(mb) < 7)
213 return ENODATA;
214
215 hdr->timestamp_delta = mbuf_read_u24_ntoh(mb);
216 hdr->length = mbuf_read_u24_ntoh(mb);
217 hdr->type_id = mbuf_read_u8(mb);
218 break;
219
220 case 2:
221 if (mbuf_get_left(mb) < 3)
222 return ENODATA;
223
224 hdr->timestamp_delta = mbuf_read_u24_ntoh(mb);
225 break;
226
227 case 3:
228 /* no payload */
229 break;
230 }
231
232 if (hdr->timestamp == TIMESTAMP_24MAX)
233 timestamp_ext = &hdr->timestamp;
234 else if (hdr->timestamp_delta == TIMESTAMP_24MAX)
235 timestamp_ext = &hdr->timestamp_delta;
236
237 if (timestamp_ext) {
238 if (mbuf_get_left(mb) < 4)
239 return ENODATA;
240
241 *timestamp_ext = ntohl(mbuf_read_u32(mb));
242 hdr->ext_ts = true;
243 }
244
245 return 0;
246}
247
248
249int rtmp_header_print(struct re_printf *pf, const struct rtmp_header *hdr)
250{
251 if (!hdr)
252 return 0;
253
254 return re_hprintf(pf,
255 "fmt %u, chunk %u, "
256 "timestamp %5u, ts_delta %2u,"
257 " len %3u, type %2u (%-14s) stream_id %u",
258 hdr->format, hdr->chunk_id, hdr->timestamp,
259 hdr->timestamp_delta, hdr->length, hdr->type_id,
260 rtmp_packet_type_name(hdr->type_id), hdr->stream_id);
261}
262
263
264const char *rtmp_packet_type_name(enum rtmp_packet_type type)
265{
266 switch (type) {
267
268 case RTMP_TYPE_SET_CHUNK_SIZE: return "Set Chunk Size";
269 case RTMP_TYPE_ACKNOWLEDGEMENT: return "Acknowledgement";
270 case RTMP_TYPE_USER_CONTROL_MSG: return "User Control Message";
271 case RTMP_TYPE_WINDOW_ACK_SIZE: return "Window Acknowledgement Size";
272 case RTMP_TYPE_SET_PEER_BANDWIDTH:return "Set Peer Bandwidth";
273 case RTMP_TYPE_AUDIO: return "Audio Message";
274 case RTMP_TYPE_VIDEO: return "Video Message";
275 case RTMP_TYPE_DATA: return "Data Message";
276 case RTMP_TYPE_AMF0: return "AMF";
277 default: return "?";
278 }
279}