Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/rtmp/dechunk.c b/src/rtmp/dechunk.c
new file mode 100644
index 0000000..df82d78
--- /dev/null
+++ b/src/rtmp/dechunk.c
@@ -0,0 +1,292 @@
+/**
+ * @file rtmp/dechunk.c Real Time Messaging Protocol (RTMP) -- Dechunking
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_list.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_net.h>
+#include <re_sa.h>
+#include <re_rtmp.h>
+#include "rtmp.h"
+
+
+enum {
+ MAX_CHUNKS = 64,
+};
+
+
+struct rtmp_chunk {
+ struct le le;
+ struct rtmp_header hdr;
+ struct mbuf *mb;
+};
+
+/** Defines the RTMP Dechunker */
+struct rtmp_dechunker {
+ struct list chunkl; /* struct rtmp_chunk */
+ size_t chunk_sz;
+ rtmp_dechunk_h *chunkh;
+ void *arg;
+};
+
+
+static void destructor(void *data)
+{
+ struct rtmp_dechunker *rd = data;
+
+ list_flush(&rd->chunkl);
+}
+
+
+static void chunk_destructor(void *data)
+{
+ struct rtmp_chunk *chunk = data;
+
+ list_unlink(&chunk->le);
+ mem_deref(chunk->mb);
+}
+
+
+static struct rtmp_chunk *create_chunk(struct list *chunkl,
+ const struct rtmp_header *hdr)
+{
+ struct rtmp_chunk *chunk;
+
+ chunk = mem_zalloc(sizeof(*chunk), chunk_destructor);
+ if (!chunk)
+ return NULL;
+
+ chunk->hdr = *hdr;
+
+ list_append(chunkl, &chunk->le, chunk);
+
+ return chunk;
+}
+
+
+static struct rtmp_chunk *find_chunk(const struct list *chunkl,
+ uint32_t chunk_id)
+{
+ struct le *le;
+
+ for (le = list_head(chunkl); le; le = le->next) {
+
+ struct rtmp_chunk *chunk = le->data;
+
+ if (chunk_id == chunk->hdr.chunk_id)
+ return chunk;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Stateful RTMP de-chunker for receiving complete messages
+ */
+int rtmp_dechunker_alloc(struct rtmp_dechunker **rdp, size_t chunk_sz,
+ rtmp_dechunk_h *chunkh, void *arg)
+{
+ struct rtmp_dechunker *rd;
+
+ if (!rdp || !chunk_sz || !chunkh)
+ return EINVAL;
+
+ rd = mem_zalloc(sizeof(*rd), destructor);
+ if (!rd)
+ return ENOMEM;
+
+ rd->chunk_sz = chunk_sz;
+
+ rd->chunkh = chunkh;
+ rd->arg = arg;
+
+ *rdp = rd;
+
+ return 0;
+}
+
+
+int rtmp_dechunker_receive(struct rtmp_dechunker *rd, struct mbuf *mb)
+{
+ struct rtmp_header hdr;
+ struct rtmp_chunk *chunk;
+ size_t chunk_sz, left, msg_len;
+ int err;
+
+ if (!rd || !mb)
+ return EINVAL;
+
+ err = rtmp_header_decode(&hdr, mb);
+ if (err)
+ return err;
+
+ /* find preceding chunk, from chunk id */
+ chunk = find_chunk(&rd->chunkl, hdr.chunk_id);
+ if (!chunk) {
+
+ /* only type 0 can create a new chunk stream */
+ if (hdr.format == 0) {
+ if (list_count(&rd->chunkl) > MAX_CHUNKS)
+ return EOVERFLOW;
+
+ chunk = create_chunk(&rd->chunkl, &hdr);
+ if (!chunk)
+ return ENOMEM;
+ }
+ else
+ return ENOENT;
+ }
+
+ switch (hdr.format) {
+
+ case 0:
+ case 1:
+ case 2:
+ if (hdr.format == 0) {
+
+ /* copy the whole header */
+ chunk->hdr = hdr;
+ }
+ else if (hdr.format == 1) {
+
+ chunk->hdr.timestamp_delta = hdr.timestamp_delta;
+ chunk->hdr.length = hdr.length;
+ chunk->hdr.type_id = hdr.type_id;
+ }
+ else if (hdr.format == 2) {
+
+ chunk->hdr.timestamp_delta = hdr.timestamp_delta;
+ }
+
+ msg_len = chunk->hdr.length;
+
+ chunk_sz = min(msg_len, rd->chunk_sz);
+
+ if (mbuf_get_left(mb) < chunk_sz)
+ return ENODATA;
+
+ mem_deref(chunk->mb);
+ chunk->mb = mbuf_alloc(msg_len);
+ if (!chunk->mb)
+ return ENOMEM;
+
+ err = mbuf_read_mem(mb, chunk->mb->buf, chunk_sz);
+ if (err)
+ return err;
+
+ chunk->mb->pos = chunk_sz;
+ chunk->mb->end = chunk_sz;
+
+ chunk->hdr.format = hdr.format;
+ chunk->hdr.ext_ts = hdr.ext_ts;
+
+ if (hdr.format == 1 || hdr.format == 2)
+ chunk->hdr.timestamp += hdr.timestamp_delta;
+ break;
+
+ case 3:
+ if (chunk->hdr.ext_ts) {
+
+ uint32_t ext_ts;
+
+ if (mbuf_get_left(mb) < 4)
+ return ENODATA;
+
+ ext_ts = ntohl(mbuf_read_u32(mb));
+
+ if (chunk->hdr.format == 0)
+ chunk->hdr.timestamp = ext_ts;
+ else
+ chunk->hdr.timestamp_delta = ext_ts;
+ }
+
+ if (!chunk->mb) {
+
+ chunk->mb = mbuf_alloc(chunk->hdr.length);
+ if (!chunk->mb)
+ return ENOMEM;
+
+ if (chunk->hdr.format == 0) {
+ chunk->hdr.timestamp_delta =
+ chunk->hdr.timestamp;
+ }
+
+ chunk->hdr.timestamp += chunk->hdr.timestamp_delta;
+ }
+
+ left = mbuf_get_space(chunk->mb);
+
+ chunk_sz = min(left, rd->chunk_sz);
+
+ if (mbuf_get_left(mb) < chunk_sz)
+ return ENODATA;
+
+ err = mbuf_read_mem(mb, mbuf_buf(chunk->mb), chunk_sz);
+ if (err)
+ return err;
+
+ chunk->mb->pos += chunk_sz;
+ chunk->mb->end += chunk_sz;
+ break;
+
+ default:
+ return EPROTO;
+ }
+
+ if (chunk->mb->pos >= chunk->mb->size) {
+
+ struct mbuf *buf;
+
+ chunk->mb->pos = 0;
+
+ buf = chunk->mb;
+ chunk->mb = NULL;
+
+ err = rd->chunkh(&chunk->hdr, buf, rd->arg);
+
+ mem_deref(buf);
+ }
+
+ return err;
+}
+
+
+void rtmp_dechunker_set_chunksize(struct rtmp_dechunker *rd, size_t chunk_sz)
+{
+ if (!rd || !chunk_sz)
+ return;
+
+ rd->chunk_sz = chunk_sz;
+}
+
+
+int rtmp_dechunker_debug(struct re_printf *pf, const struct rtmp_dechunker *rd)
+{
+ struct le *le;
+ int err;
+
+ if (!rd)
+ return 0;
+
+ err = re_hprintf(pf, "Dechunker Debug:\n");
+
+ err |= re_hprintf(pf, "chunk list: (%u)\n", list_count(&rd->chunkl));
+
+ for (le = rd->chunkl.head; le; le = le->next) {
+
+ const struct rtmp_chunk *msg = le->data;
+
+ err |= re_hprintf(pf, ".. %H\n",
+ rtmp_header_print, &msg->hdr);
+ }
+
+ err |= re_hprintf(pf, "\n");
+
+ return err;
+}