diff --git a/.github/workflows/run-on-arch.yml b/.github/workflows/run-on-arch.yml index 5a2c66137..249ffc1b4 100644 --- a/.github/workflows/run-on-arch.yml +++ b/.github/workflows/run-on-arch.yml @@ -17,6 +17,8 @@ jobs: include: - arch: aarch64 distro: bullseye + - arch: armv7 + distro: ubuntu22.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 86f23d5cf..d444c1e00 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -30,6 +30,10 @@ jobs: run: | sudo sed -i 's/azure\./de\./' /etc/apt/sources.list + - name: mmap rnd_bits workaround + run: | + sudo sysctl -w vm.mmap_rnd_bits=28 + - name: install packages run: | sudo apt-get update && sudo apt-get install -y ninja-build diff --git a/include/re.h b/include/re.h index 2167f9bba..f00f1bc2b 100644 --- a/include/re.h +++ b/include/re.h @@ -29,6 +29,7 @@ extern "C" { #include "re_convert.h" #include "re_crc32.h" #include "re_dns.h" +#include "re_h264.h" #include "re_hash.h" #include "re_hmac.h" #include "re_http.h" diff --git a/include/re_av1.h b/include/re_av1.h index 44e37fa1f..16476db8e 100644 --- a/include/re_av1.h +++ b/include/re_av1.h @@ -45,6 +45,7 @@ int av1_obu_print(struct re_printf *pf, const struct av1_obu_hdr *hdr); unsigned av1_obu_count(const uint8_t *buf, size_t size); unsigned av1_obu_count_rtp(const uint8_t *buf, size_t size); const char *av1_obu_name(enum obu_type type); +bool obu_allowed_rtp(enum obu_type type); /* @@ -59,6 +60,9 @@ typedef int (av1_packet_h)(bool marker, uint64_t rtp_ts, int av1_packetize_high(bool *newp, bool marker, uint64_t rtp_ts, const uint8_t *buf, size_t len, size_t maxlen, av1_packet_h *pkth, void *arg); +int av1_packetize_one_w(bool *newp, bool marker, uint64_t rtp_ts, + const uint8_t *buf, size_t len, size_t maxlen, + av1_packet_h *pkth, void *arg); enum { diff --git a/include/re_sdp.h b/include/re_sdp.h index 8ad3ab774..c5a84044b 100644 --- a/include/re_sdp.h +++ b/include/re_sdp.h @@ -131,6 +131,8 @@ const char *sdp_media_rattr(const struct sdp_media *m, const char *name); const char *sdp_media_session_rattr(const struct sdp_media *m, const struct sdp_session *sess, const char *name); +const char *sdp_media_lattr_apply(const struct sdp_media *m, const char *name, + sdp_attr_h *attrh, void *arg); const char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name, sdp_attr_h *attrh, void *arg); const char *sdp_media_name(const struct sdp_media *m); diff --git a/include/re_tls.h b/include/re_tls.h index 61e28548f..eb944e57e 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -28,6 +28,13 @@ enum tls_keytype { TLS_KEYTYPE_EC, }; +enum tls_resume_mode { + TLS_RESUMPTION_NONE = 0, + TLS_RESUMPTION_IDS = (1 << 0), + TLS_RESUMPTION_TICKETS = (1 << 1), + TLS_RESUMPTION_ALL = TLS_RESUMPTION_IDS | TLS_RESUMPTION_TICKETS, +}; + struct tls_conn_d { int (*verifyh) (int ok, void *arg); void *arg; @@ -75,6 +82,7 @@ int tls_get_issuer(struct tls *tls, struct mbuf *mb); int tls_get_subject(struct tls *tls, struct mbuf *mb); void tls_disable_verify_server(struct tls *tls); void tls_enable_verify_client(struct tls *tls, bool enable); +int tls_set_resumption(struct tls *tls, const enum tls_resume_mode mode); int tls_set_min_proto_version(struct tls *tls, int version); int tls_set_max_proto_version(struct tls *tls, int version); diff --git a/src/av1/obu.c b/src/av1/obu.c index 74a2c3c7f..b32ffd0fc 100644 --- a/src/av1/obu.c +++ b/src/av1/obu.c @@ -16,7 +16,7 @@ #include -static bool obu_allowed_rtp(enum obu_type type) +bool obu_allowed_rtp(enum obu_type type) { switch (type) { diff --git a/src/av1/pkt.c b/src/av1/pkt.c index 658f6c206..9df14268a 100644 --- a/src/av1/pkt.c +++ b/src/av1/pkt.c @@ -22,6 +22,35 @@ enum { }; +/* + * Calculate length of LEB128 field + * + * Add high 1 bits on all but last (most significant) group to form bytes + */ +static size_t leb128_calc_size(uint64_t value) +{ + size_t bytes = 1; + + /* Bit7: 1=more bytes coming, 0=complete */ + while (value >= 0x80) { + + ++bytes; + + value >>= 7; + } + + return bytes; +} + + +/* + * Z: MUST be set to 1 if the first OBU element is an OBU fragment that is a + * continuation of an OBU fragment from the previous packet, and MUST be + * set to 0 otherwise. + * + * Y: MUST be set to 1 if the last OBU element is an OBU fragment that will + * continue in the next packet, and MUST be set to 0 otherwise. + */ static void hdr_encode(uint8_t hdr[AV1_AGGR_HDR_SIZE], bool z, bool y, uint8_t w, bool n) { @@ -51,7 +80,7 @@ static struct mbuf *encode_obu(uint8_t type, const uint8_t *p, size_t len) static int copy_obus(struct mbuf *mb_pkt, const uint8_t *buf, size_t size, - bool w0) + bool w0, size_t maxlen, unsigned *small_obus) { struct mbuf wrap = { .buf = (uint8_t *)buf, @@ -60,12 +89,16 @@ static int copy_obus(struct mbuf *mb_pkt, const uint8_t *buf, size_t size, .end = size }; struct mbuf *mb_obu = NULL; + size_t accum = AV1_AGGR_HDR_SIZE; + unsigned count = 0; + enum { OBU_HEADER_SIZE=1 }; int err = 0; while (mbuf_get_left(&wrap) >= 2) { struct av1_obu_hdr hdr; bool last; + size_t tmp; err = av1_obu_decode(&hdr, &wrap); if (err) { @@ -107,6 +140,17 @@ static int copy_obus(struct mbuf *mb_pkt, const uint8_t *buf, size_t size, if (err) goto out; + /* Count number of small OBUs that fits */ + + tmp = OBU_HEADER_SIZE + + leb128_calc_size(hdr.size) + + hdr.size; + + accum += tmp; + + if (accum < maxlen) { + ++count; + } break; case AV1_OBU_TEMPORAL_DELIMITER: @@ -125,6 +169,9 @@ static int copy_obus(struct mbuf *mb_pkt, const uint8_t *buf, size_t size, mb_obu = mem_deref(mb_obu); } + if (small_obus) + *small_obus = count; + out: mem_deref(mb_obu); return err; @@ -134,13 +181,13 @@ static int copy_obus(struct mbuf *mb_pkt, const uint8_t *buf, size_t size, static int av1_packetize_internal(bool *newp, bool marker, uint64_t rtp_ts, const uint8_t *buf, size_t len, size_t maxlen, uint8_t w, + bool use_w_field, unsigned small_obus, av1_packet_h *pkth, void *arg) { uint8_t hdr[AV1_AGGR_HDR_SIZE]; - bool cont = false; + bool z_cont = false; int err = 0; - if (w > 3) { DEBUG_WARNING("w too large\n"); return EPROTO; @@ -150,21 +197,25 @@ static int av1_packetize_internal(bool *newp, bool marker, uint64_t rtp_ts, while (len > maxlen) { - hdr_encode(hdr, cont, true, w, *newp); + hdr_encode(hdr, z_cont, true, w, *newp); *newp = false; err |= pkth(false, rtp_ts, hdr, sizeof(hdr), buf, maxlen, arg); buf += maxlen; len -= maxlen; - cont = true; + z_cont = true; /* If OBUs are fragmented */ - if (w == 2) - w = 1; + if (use_w_field && small_obus > 0) { + + if (w==2 || w==3) { + w -= small_obus; + } + } } - hdr_encode(hdr, cont, false, w, *newp); + hdr_encode(hdr, z_cont, false, w, *newp); *newp = false; err |= pkth(marker, rtp_ts, hdr, sizeof(hdr), buf, len, arg); @@ -212,18 +263,95 @@ int av1_packetize_high(bool *newp, bool marker, uint64_t rtp_ts, w = count; } - err = copy_obus(mb_pkt, buf, len, count > MAX_OBUS); + bool use_w_field = count <= MAX_OBUS; + unsigned small_obus = 0; + + err = copy_obus(mb_pkt, buf, len, count > MAX_OBUS, + maxlen, &small_obus); if (err) goto out; err = av1_packetize_internal(newp, marker, rtp_ts, mb_pkt->buf, mb_pkt->end, maxlen, - w, + w, use_w_field, small_obus, pkth, arg); - if (err) - goto out; out: mem_deref(mb_pkt); return err; } + + +/** + * Packetize an AV1 bitstream with one or more OBUs, using W=1 mode + * + * @param newp Pointer to new stream flag + * @param marker Set marker bit + * @param rtp_ts RTP timestamp + * @param buf Input buffer + * @param len Buffer length + * @param maxlen Maximum RTP packet size + * @param pkth Packet handler + * @param arg Handler argument + * + * @return 0 if success, otherwise errorcode + * + * W: two bit field that describes the number of OBU elements in the packet. + * This field MUST be set equal to 0 or equal to the number of OBU elements + * contained in the packet. If set to 0, each OBU element MUST be preceded + * by a length field. + * If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element MUST NOT be + * preceded by a length field. + * + */ +int av1_packetize_one_w(bool *newp, bool marker, uint64_t rtp_ts, + const uint8_t *buf, size_t len, size_t maxlen, + av1_packet_h *pkth, void *arg) +{ + struct mbuf wrap = { + .buf = (uint8_t *)buf, + .size = len, + .pos = 0, + .end = len + }; + int err = 0; + + while (mbuf_get_left(&wrap) >= 2) { + + struct av1_obu_hdr hdr; + size_t start = wrap.pos; + + err = av1_obu_decode(&hdr, &wrap); + if (err) { + DEBUG_WARNING("av1: encode: hdr dec error (%m)\n", + err); + return err; + } + + if (obu_allowed_rtp(hdr.type)) { + + size_t header_size = wrap.pos - start; + size_t total_size = header_size + hdr.size; + bool last = (hdr.size == mbuf_get_left(&wrap)); + bool use_w_field = true; + + err = av1_packetize_internal(newp, + marker && last, + rtp_ts, + &wrap.buf[start], + total_size, + maxlen, + 1, + use_w_field, + 0, + pkth, + arg); + if (err) + return err; + } + + mbuf_advance(&wrap, hdr.size); + } + + return err; +} diff --git a/src/h264/nal.c b/src/h264/nal.c index 68623897e..180cedd33 100644 --- a/src/h264/nal.c +++ b/src/h264/nal.c @@ -155,7 +155,8 @@ int h264_fu_hdr_decode(struct h264_fu *fu, struct mbuf *mb) * * @note: copied from ffmpeg source */ -const uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end) +static const uint8_t *h264_find_startcode_int(const uint8_t *p, + const uint8_t *end) { const uint8_t *a = p + 4 - ((size_t)p & 3); @@ -191,6 +192,18 @@ const uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end) } +const uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end) +{ + const uint8_t *out = h264_find_startcode_int(p, end); + + /* check for 4-byte startcode */ + if (pr.fb.fci.gnackv[i].blp); } } + else if (msg->hdr.count == RTCP_RTPFB_TWCC) { + const struct twcc *twcc = msg->r.fb.fci.twccv; + + err |= re_hprintf(pf, + " TWCC" + " base_seq=%u" + " pkt_status_count=%u" + " ref_time=%u" + " fb_pkt_count=%u" + , + twcc->seq, + twcc->count, + twcc->reftime, + twcc->fbcount); + } break; case RTCP_PSFB: diff --git a/src/sdp/media.c b/src/sdp/media.c index 19ae3cc3c..7ac0182a4 100644 --- a/src/sdp/media.c +++ b/src/sdp/media.c @@ -911,6 +911,26 @@ const char *sdp_media_session_rattr(const struct sdp_media *m, } +/** + * Apply a function handler to all matching local attributes + * + * @param m SDP Media line + * @param name Attribute name + * @param attrh Attribute handler + * @param arg Handler argument + * + * @return Attribute value if match + */ +const char *sdp_media_lattr_apply(const struct sdp_media *m, const char *name, + sdp_attr_h *attrh, void *arg) +{ + if (!m) + return NULL; + + return sdp_attr_apply(&m->lattrl, name, attrh, arg); +} + + /** * Apply a function handler to all matching remote attributes * diff --git a/src/thread/posix.c b/src/thread/posix.c index 7cc3822ff..6a70cac84 100644 --- a/src/thread/posix.c +++ b/src/thread/posix.c @@ -16,11 +16,12 @@ struct thread { static void *handler(void *p) { - struct thread th = *(struct thread *)p; + struct thread *th = p; - mem_deref(p); + int ret = th->func(th->arg); + mem_deref(th); - return (void *)(intptr_t)th.func(th.arg); + return (void *)(intptr_t)ret; } diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index a5b983ae9..1d9fb50cb 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -2190,3 +2190,45 @@ int tls_verify_client_post_handshake(struct tls_conn *tc) return err; } + + +/** + * Set TLS session resumption mode + * + * @param tls TLS Object + * @param mode TLS session resumption mode + * + * @return 0 if success, otherwise errorcode + */ +int tls_set_resumption(struct tls *tls, const enum tls_resume_mode mode) +{ + long ok = 1; + + if (!tls) + return EINVAL; + + if (mode & TLS_RESUMPTION_IDS) { + ok = SSL_CTX_set_session_cache_mode(tls->ctx, + SSL_SESS_CACHE_SERVER); + } + else { + ok = SSL_CTX_set_session_cache_mode(tls->ctx, + SSL_SESS_CACHE_OFF); + } + + if (mode & TLS_RESUMPTION_TICKETS) { + ok |= SSL_CTX_clear_options(tls->ctx, SSL_OP_NO_TICKET); + ok |= SSL_CTX_set_num_tickets(tls->ctx, 2); + } + else { + ok |= SSL_CTX_set_options(tls->ctx, SSL_OP_NO_TICKET); + ok |= SSL_CTX_set_num_tickets(tls->ctx, 0); + } + + if (!ok) { + ERR_clear_error(); + return EFAULT; + } + + return 0; +} diff --git a/test/h264.c b/test/h264.c index e23fc48ec..d10738937 100644 --- a/test/h264.c +++ b/test/h264.c @@ -6,7 +6,6 @@ #include #include -#include #include #include "test.h" @@ -16,6 +15,9 @@ #include +enum { DUMMY_TS = 36000 }; + + #if 0 static void dump_annexb(const uint8_t *start, size_t size) { @@ -87,7 +89,7 @@ static int test_h264_stap_a_encode(void) 0x00, 0x00, 0x01, 0x65, 0xb8, 0x00, 0x04, 0x00, 0x00, 0x05, 0x39, }; -#define MAX_NRI 3 + enum { MAX_NRI = 3 }; struct mbuf *mb_pkt = mbuf_alloc(256); struct mbuf *mb_frame = mbuf_alloc(256); struct h264_nal_header hdr; @@ -176,11 +178,10 @@ static int test_h264_stap_a_decode(void) int test_h264(void) { struct h264_nal_header hdr, hdr2; - struct mbuf *mb; static const uint8_t nal = 0x25; int err; - mb = mbuf_alloc(1); + struct mbuf *mb = mbuf_alloc(1); if (!mb) return ENOMEM; @@ -486,6 +487,7 @@ struct state { struct mbuf *mb; size_t frag_start; bool frag; + bool long_startcode; /* test */ uint8_t buf[256]; @@ -505,8 +507,11 @@ static void fragment_rewind(struct state *vds) static int depack_handle_h264(struct state *st, bool marker, struct mbuf *src) { - static const uint8_t nal_seq[3] = {0, 0, 1}; + static const uint8_t nal_seq3[3] = {0, 0, 1}; + static const uint8_t nal_seq4[4] = {0, 0, 0, 1}; struct h264_nal_header h264_hdr; + size_t nal_seq_len = st->long_startcode ? + sizeof(nal_seq4) : sizeof(nal_seq3); int err; err = h264_nal_header_decode(&h264_hdr, src); @@ -532,7 +537,9 @@ static int depack_handle_h264(struct state *st, bool marker, --src->pos; /* prepend H.264 NAL start sequence */ - err = mbuf_write_mem(st->mb, nal_seq, sizeof(nal_seq)); + err = mbuf_write_mem(st->mb, + st->long_startcode ? nal_seq4 : nal_seq3, + nal_seq_len); err |= mbuf_write_mem(st->mb, mbuf_buf(src), mbuf_get_left(src)); @@ -560,7 +567,9 @@ static int depack_handle_h264(struct state *st, bool marker, st->frag = true; /* prepend H.264 NAL start sequence */ - mbuf_write_mem(st->mb, nal_seq, sizeof(nal_seq)); + mbuf_write_mem(st->mb, + st->long_startcode ? nal_seq4 : nal_seq3, + nal_seq_len); /* encode NAL header back to buffer */ err = h264_nal_header_encode(st->mb, &h264_hdr); @@ -600,7 +609,8 @@ static int depack_handle_h264(struct state *st, bool marker, --src->pos; err = mbuf_write_mem(st->mb, - nal_seq, sizeof(nal_seq)); + st->long_startcode ? nal_seq4 : nal_seq3, + nal_seq_len); err |= mbuf_write_mem(st->mb, mbuf_buf(src), len); if (err) goto out; @@ -629,9 +639,6 @@ static int depack_handle_h264(struct state *st, bool marker, } -enum { DUMMY_TS = 36000 }; - - static int packet_handler(bool marker, uint64_t rtp_ts, const uint8_t *hdr, size_t hdr_len, const uint8_t *pld, size_t pld_len, @@ -663,21 +670,18 @@ static int packet_handler(bool marker, uint64_t rtp_ts, } -/* bitstream in Annex-B format (with startcode 00 00 01) */ -static const char *bitstream = "000001650010e2238712983719283719823798"; - - -int test_h264_packet(void) +static int test_h264_packet_base(const char *bs, bool long_startcode) { struct state state; - const size_t pktsize = 8; + const size_t MAX_PKTSIZE = 8; int err; memset(&state, 0, sizeof(state)); - state.len = strlen(bitstream)/2; + state.long_startcode = long_startcode; + state.len = strlen(bs)/2; - err = str_hex(state.buf, state.len, bitstream); + err = str_hex(state.buf, state.len, bs); if (err) return err; @@ -685,7 +689,7 @@ int test_h264_packet(void) if (!state.mb) return ENOMEM; - err = h264_packetize(DUMMY_TS, state.buf, state.len, pktsize, + err = h264_packetize(DUMMY_TS, state.buf, state.len, MAX_PKTSIZE, packet_handler, &state); if (err) goto out; @@ -698,3 +702,32 @@ int test_h264_packet(void) return err; } + + +/* bitstream in Annex-B format (with startcode 00 00 01) */ +static const char *bitstream = + "0000016701020304" + "0000016801020304" + "000001650010e2238712983719283719823798"; + + +/* bitstream in Annex-B format (with startcode 00 00 00 01) */ +static const char *bitstream_long = + "000000016701020304" + "000000016801020304" + "00000001650010e2238712983719283719823798"; + + +int test_h264_packet(void) +{ + int err; + + err = test_h264_packet_base(bitstream, false); + TEST_ERR(err); + + err = test_h264_packet_base(bitstream_long, true); + TEST_ERR(err); + + out: + return err; +}