#include "rtp-ext.h" #include "rtp-header.h" #include #include #include #include static const struct rtp_ext_uri_t sc_rtpexts[] = { {RTP_HDREXT_PADDING, ""}, // https://datatracker.ietf.org/doc/html/rfc6464 {RTP_HDREXT_SSRC_AUDIO_LEVEL_ID, "urn:ietf:params:rtp-hdrext:ssrc-audio-level"}, // https://datatracker.ietf.org/doc/html/rfc6465 {RTP_HDREXT_CSRC_AUDIO_LEVEL_ID, "urn:ietf:params:rtp-hdrext:csrc-audio-level"}, // https://datatracker.ietf.org/doc/html/draft-ietf-avtext-framemarking-13 //{RTP_HDREXT_FRAME_MARKING_ID, "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07"}, {RTP_HDREXT_FRAME_MARKING_ID, "urn:ietf:params:rtp-hdrext:framemarking"}, // https://datatracker.ietf.org/doc/html/rfc8843#section-16.2 {RTP_HDREXT_SDES_MID_ID, "urn:ietf:params:rtp-hdrext:sdes:mid"}, // https://datatracker.ietf.org/doc/html/rfc8852#section-4 {RTP_HDREXT_SDES_RTP_STREAM_ID, "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, {RTP_HDREXT_SDES_REPAIRED_RTP_STREAM_ID, "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"}, // https://datatracker.ietf.org/doc/html/rfc5450 {RTP_HDREXT_TOFFSET_ID, "urn:ietf:params:rtp-hdrext:toffset"}, // https://www.arib.or.jp/english/html/overview/doc/STD-T63V12_00/5_Appendix/Rel13/26/26114-d30.pdf {RTP_HDREXT_VIDEO_ORIENTATION_ID, "urn:3gpp:video-orientation"}, // // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-send-time {RTP_HDREXT_ABSOLUTE_SEND_TIME_ID, "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-capture-time/ {RTP_HDREXT_ABSOLUTE_CAPTURE_TIME_ID, "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/transport-wide-cc-02/ {RTP_HDREXT_TRANSPORT_WIDE_CC_ID_01, "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {RTP_HDREXT_TRANSPORT_WIDE_CC_ID, "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-timing {RTP_HDREXT_VIDEO_TIMING_ID, "http://www.webrtc.org/experiments/rtp-hdrext/video-timing"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/playout-delay {RTP_HDREXT_PLAYOUT_DELAY_ID, "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"}, {RTP_HDREXT_ONE_BYTE_RESERVED, ""}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/color-space {RTP_HDREXT_COLOR_SPACE_ID, "http://www.webrtc.org/experiments/rtp-hdrext/color-space"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-content-type {RTP_HDREXT_VIDEO_CONTENT_TYPE_ID, "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/inband-cn/ {RTP_HDREXT_INBAND_CN_ID, "http://www.webrtc.org/experiments/rtp-hdrext/inband-cn"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-frame-tracking-id/ {RTP_HDREXT_VIDEO_FRAME_TRACKING_ID, "http://www.webrtc.org/experiments/rtp-hdrext/video-frame-tracking-id"}, // https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-layers-allocation00/ {RTP_HDREXT_VIDEO_LAYERS_ALLOCATION_ID, "http://www.webrtc.org/experiments/rtp-hdrext/video-layers-allocation00"}, //{RTP_HDREXT_GENERIC_FRAME_DESCRIPTOR_00, "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00"}, //{RTP_HDREXT_GENERIC_FRAME_DESCRIPTOR_02, "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-02"}, //{RTP_HDREXT_ENCRYPT, "urn:ietf:params:rtp-hdrext:encrypt"}, {0, NULL}, }; const struct rtp_ext_uri_t* rtp_ext_list() { return sc_rtpexts; } const struct rtp_ext_uri_t* rtp_ext_find_uri(const char* uri) { int i; for (i = 0; i < sizeof(sc_rtpexts) / sizeof(sc_rtpexts[0]); i++) { if (0 == strcmp(sc_rtpexts[i].uri, uri)) return &sc_rtpexts[i]; } return NULL; } static int rtp_ext_read_one_byte(const uint8_t* data, int bytes, struct rtp_ext_data_t exts[256]) { int off; uint8_t id; uint8_t len; for (off = 0; off < bytes; off += len + 1) { id = data[off] >> 4; len = (data[off] & 0x0f) + 1; if (bytes > 0xFFFF || off + len > bytes || (RTP_HDREXT_PADDING == id && 1 != len)) return -1; // invalid if (RTP_HDREXT_PADDING == data[off]) continue; else if (id == RTP_HDREXT_ONE_BYTE_RESERVED) break; // one-byte header extension reserver id exts[id].id = id; exts[id].len = len; exts[id].off = off + 1; // data only } return 0; } static int rtp_ext_write_one_byte(const uint8_t* extension, const struct rtp_ext_data_t* exts, int count, uint8_t* data, int bytes) { int i, off; for (i = off = 0; i < count && off < bytes; off += exts[i++].len) { if (RTP_HDREXT_PADDING == exts[i].id) { assert(exts[i].len == 0); continue; } // 15: one-byte header extension reserver id if (exts[i].id >= RTP_HDREXT_ONE_BYTE_RESERVED || exts[i].len < 1 || exts[i].len > 16 || (int)exts[i].len + off >= bytes) { assert(0); return -EINVAL; } data[off] = ((uint8_t)exts[i].id << 4) | ((uint8_t)exts[i].len - 1); memcpy(data + off + 1, extension + exts[i].off, exts[i].len); } if (i < count || ( (off % 4 != 0) && (off + 3)/4*4 >= bytes) ) return -E2BIG; while(off % 4 != 0) data[off++] = RTP_HDREXT_PADDING; return off; } static int rtp_ext_read_two_byte(const uint8_t* data, int bytes, struct rtp_ext_data_t exts[256]) { int off; uint8_t id; uint8_t len; for (off = 0; off < bytes; off += len) { id = data[off++]; if (RTP_HDREXT_PADDING == id) { len = 0; continue; } if (off >= bytes) return -EINVAL; len = data[off++]; if (off + len > bytes) return -EINVAL;; // invalid exts[id].id = id; exts[id].len = len; exts[id].off = off; // data only } return 0; } static int rtp_ext_write_two_byte(const uint8_t* extension, const struct rtp_ext_data_t* exts, int count, uint8_t* data, int bytes) { int i, off; for (i = off = 0; i < count && off < bytes; off += exts[i++].len) { if (RTP_HDREXT_PADDING == exts[i].id) { assert(exts[i].len == 0); continue; } if ((int)exts[i].len + off + 2 >= bytes) { assert(0); return -EINVAL; } data[off++] = (uint8_t)exts[i].id; data[off++] = (uint8_t)exts[i].len; memcpy(data + off, extension + exts[i].off, exts[i].len); } if (i < count || ((off % 4 != 0) && (off + 3) / 4 * 4 >= bytes)) return -E2BIG; while (off % 4 != 0) data[off++] = RTP_HDREXT_PADDING; return off; } int rtp_ext_read(uint16_t profile, const uint8_t* data, int bytes, struct rtp_ext_data_t exts[256]) { // caller to do // memset(exts, 0, sizeof(exts)); if(RTP_HDREXT_PROFILE_ONE_BYTE == profile) return rtp_ext_read_one_byte(data, bytes, exts); else if (RTP_HDREXT_PROFILE_TWO_BYTE == (profile & RTP_HDREXT_PROFILE_TWO_BYTE_FILTER)) return rtp_ext_read_two_byte(data, bytes, exts); return 0; // ignore } int rtp_ext_write(uint16_t profile, const uint8_t* extension, const struct rtp_ext_data_t* exts, int count, uint8_t* data, int bytes) { int i; if (0 == profile) { profile = RTP_HDREXT_PROFILE_ONE_BYTE; for (i = 0; i < count; i++) { // 15: one-byte header extension reserver id if (exts[i].len >= 16 || RTP_HDREXT_ONE_BYTE_RESERVED == exts[i].id) profile = RTP_HDREXT_PROFILE_TWO_BYTE; } } if (RTP_HDREXT_PROFILE_ONE_BYTE == profile) return rtp_ext_write_one_byte(extension, exts, count, data, bytes); else if (RTP_HDREXT_PROFILE_TWO_BYTE == (profile & RTP_HDREXT_PROFILE_TWO_BYTE_FILTER)) return rtp_ext_write_two_byte(extension, exts, count, data, bytes); return -1; // ignore } #if defined(DEBUG) || defined(_DEBUG) static void rtp_ext_read_onebyte_test(void) { const uint8_t data[] = { 0x22, 0xca, 0x4e, 0x36, 0x31, 0x00, 0x01, 0x40, 0x30, 0x10, 0xb2, 0x00 }; struct rtp_ext_data_t exts[256]; int i; memset(exts, 0, sizeof(exts)); assert(0 == rtp_ext_read(RTP_HDREXT_PROFILE_ONE_BYTE, data, sizeof(data), exts)); assert(exts[2].len == 3 && exts[3].len == 2 && exts[4].len == 1 && exts[1].len == 1); for (i = 0; i < sizeof(exts) / sizeof(exts[0]); i++) { if (i == 1 || i == 2 || i == 3 || i == 4) continue; assert(exts[i].id == 0 && exts[i].len == 0); } } static void rtp_ext_read_twobyte_test(void) { } void rtp_ext_read_test(void) { rtp_ext_read_onebyte_test(); rtp_ext_read_twobyte_test(); } #endif