From 60f60cc76e43197a0825ffac9aff0b7007a94175 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 20 Nov 2013 12:57:17 +0100 Subject: Framework API now complete in its first iteration. So far with support for raw, opus and amrwb. --- src/Makefile.am | 19 ++- src/asc2bin.cc | 64 ++++++++++ src/asc2bin.h | 50 ++++++++ src/compat.h | 37 ++++++ src/lrtp.cc | 273 +++++++++++++++++++++++++++++++++++++++-- src/lrtp.h | 104 ++++++++++++++++ src/lrtp_profiles.h | 72 +++++++++++ src/rtp.cc | 262 ++++++++++++++++++++++++++++++++++++++++ src/rtp.h | 116 ++++++++++++++++++ src/rtp_profile.h | 86 +++++++++++++ src/rtp_profile_amrwb.cc | 307 +++++++++++++++++++++++++++++++++++++++++++++++ src/rtp_profile_amrwb.h | 39 ++++++ src/rtp_profile_opus.cc | 188 +++++++++++++++++++++++++++++ src/rtp_profile_opus.h | 39 ++++++ src/rtp_profile_raw.cc | 123 +++++++++++++++++++ src/rtp_profile_raw.h | 39 ++++++ src/srtp.cc | 230 +++++++++++++++++++++++++++++++++++ src/srtp.h | 54 +++++++++ 18 files changed, 2086 insertions(+), 16 deletions(-) create mode 100644 src/asc2bin.cc create mode 100644 src/asc2bin.h create mode 100644 src/compat.h create mode 100644 src/lrtp_profiles.h create mode 100644 src/rtp.cc create mode 100644 src/rtp.h create mode 100644 src/rtp_profile.h create mode 100644 src/rtp_profile_amrwb.cc create mode 100644 src/rtp_profile_amrwb.h create mode 100644 src/rtp_profile_opus.cc create mode 100644 src/rtp_profile_opus.h create mode 100644 src/rtp_profile_raw.cc create mode 100644 src/rtp_profile_raw.h create mode 100644 src/srtp.cc create mode 100644 src/srtp.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 594b257..15a201f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,10 +5,23 @@ liblrtp_la_LIBADD = $(SRTP_LIBS) liblrtp_la_CXXFLAGS = $(SRTP_CXXFLAGS) liblrtp_la_SOURCES = \ - lrtp.cc + lrtp.cc \ + rtp.cc \ + srtp.cc \ + rtp_profile_amrwb.cc \ + rtp_profile_opus.cc \ + rtp_profile_raw.cc \ + asc2bin.cc include_HEADERS = \ - lrtp.h + lrtp.h \ + lrtp_profiles.h -EXTRA_DIST = +EXTRA_DIST = \ + rtp_profile_amrwb.h \ + rtp_profile_opus.h \ + rtp_profile_raw.h \ + rtp.h \ + srtp.h \ + asc2bin.h diff --git a/src/asc2bin.cc b/src/asc2bin.cc new file mode 100644 index 0000000..65efc31 --- /dev/null +++ b/src/asc2bin.cc @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * asc2bin.cc + * + * Thu Sep 5 11:12:50 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "asc2bin.h" + +static int asc2nibble(unsigned char c) +{ + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'a' && c <= 'f') return c - 'a' + 0xa; + if(c >= 'A' && c <= 'F') return c - 'A' + 0xa; + return -1; +} + +ssize_t asc2bin(char *raw, size_t rawsz, const char *hex, size_t hexlen) +{ + if(hexlen % 2 != 0) return -1; + if(rawsz < hexlen / 2) return -1; + + unsigned char val; + int nibble; + size_t size = 0; + + while(size < hexlen / 2) { + nibble = asc2nibble(hex[0]); + if(nibble == -1) return -1; + val = (nibble << 4); + + nibble = asc2nibble(hex[1]); + if(nibble == -1) return -1; + val |= (nibble & 0xff); + + *raw = val; + raw++; + size++; + + hex += 2; + } + + return (ssize_t)size; +} diff --git a/src/asc2bin.h b/src/asc2bin.h new file mode 100644 index 0000000..42c3b54 --- /dev/null +++ b/src/asc2bin.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * asc2bin.h + * + * Thu Sep 5 11:12:50 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_ASC2BIN_H__ +#define __LRTP_ASC2BIN_H__ + +#include + +#include "compat.h" + +/** + * asc2bin converts a hexadecimal string to a raw octet string. + * @param raw The char buffer that will contain the result. + * @param rawsz The size of the output buffer 'raw'. This size must be at + * least hexlen / 2. + * @param hex The input hex string. + * @param hexlen The length of the hex string. + * @return The length of the resulting raw data. -1 on error. + * Errors are one of the following: + * - Hex string contains invalid hex character (0-9, a-f and A-F allowed). + * - rawsz is less than hexlen / 2. + * - hexlen is not a multiplum of 2 (odd hex string lengths not supported). + */ +ssize_t asc2bin(char *raw, size_t rawsz, const char *hex, size_t hexlen); + +#endif/*__LRTP_ASC2BIN_H__*/ diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 0000000..70a6286 --- /dev/null +++ b/src/compat.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * compat.h + * + * Thu Sep 5 11:14:38 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_COMPAT_H__ +#define __LRTP_COMPAT_H__ + +#ifdef WIN32 +typedef signed int ssize_t; +#else + +#endif + +#endif/*__LRTP_COMPAT_H__*/ diff --git a/src/lrtp.cc b/src/lrtp.cc index 36da6f0..1f0b11d 100644 --- a/src/lrtp.cc +++ b/src/lrtp.cc @@ -27,20 +27,267 @@ */ #include "lrtp.h" -#ifdef TEST_LRTP -//Additional dependency files -//deps: -//Required cflags (autoconf vars may be used) -//cflags: -//Required link options (autoconf vars may be used) -//libs: -#include "test.h" +#include +#include +#include +#include -TEST_BEGIN; +#include "rtp_profile.h" +#include "rtp_profile_amrwb.h" +#include "rtp_profile_opus.h" +#include "rtp_profile_raw.h" -// TODO: Put some testcode here (see test.h for usable macros). -TEST_TRUE(false, "No tests yet!"); +#include "srtp.h" -TEST_END; +#ifdef __cplusplus +extern "C" { +#endif -#endif/*TEST_LRTP*/ +typedef std::map profile_map_t; + +struct lrtp_t { + SRTP *srtp; + csrc_t ssrc; + unsigned short seq; + profile_map_t profiles; + + csrc_t next_csrc; // for frame iterator (see get_next_frame) + + std::list framelist; +}; + +typedef struct { + lrtp_profile_id_t id; + struct lrtp_profile_t *(*create)(struct lrtp_t *lrtp, unsigned int csrc, + va_list ap); +} profile_class_t; + +static const profile_class_t registered_profiles[] = { + { PROFILE_AMRWB, profile_amrwb_create }, + { PROFILE_OPUS, profile_opus_create }, + { PROFILE_RAW, profile_raw_create }, + { } +}; + +EXPORT +struct lrtp_t *lrtp_init(const char *key, unsigned int ssrc) +{ + struct lrtp_t *lrtp = new struct lrtp_t; + + lrtp->srtp = new SRTP(key, ssrc); + lrtp->ssrc = ssrc; + lrtp->seq = 0; + + return lrtp; +} + +EXPORT +void lrtp_close(struct lrtp_t *lrtp) +{ + delete lrtp->srtp; + delete lrtp; +} + +EXPORT +struct lrtp_profile_t *lrtp_create_profile(struct lrtp_t *lrtp, + lrtp_profile_id_t profile_id, + unsigned int csrc, ...) +{ + struct lrtp_profile_t *profile = NULL; + + if(lrtp->profiles.find(csrc) != lrtp->profiles.end()) { + // TODO: CSRC already active + printf("ERROR: CSRC already active\n"); + return NULL; + } + + va_list ap; + va_start(ap, csrc); + + const profile_class_t *p = registered_profiles; + while(p->create) { + if(p->id == profile_id) { + profile = p->create(lrtp, csrc, ap); + profile->process_finished = NULL; // TODO: Figure out a way to set non-profile specific options. + break; + } + p++; + } + + va_end(ap); + + if(profile) { + profile->id = PROFILE_RAW; + profile->lrtp = lrtp; + profile->csrc = csrc; + + lrtp->profiles[csrc] = profile; + } + + return profile; +} + +EXPORT +void lrtp_destroy_profile(struct lrtp_t *lrtp, unsigned int csrc) +{ + if(lrtp->profiles.find(csrc) == lrtp->profiles.end()) { + // TODO: CSRC not found + printf("ERROR: CSRC not found\n"); + return; + } + + struct lrtp_profile_t *profile = lrtp->profiles[csrc]; + profile->destroy(profile); + lrtp->profiles.erase(csrc); +} + +EXPORT +int lrtp_enqueue_frame(struct lrtp_profile_t *profile, + const char *data, size_t size) +{ + inputframe_t *frame = new inputframe_t(); + frame->data = data; + frame->size = size; + frame->offset = 0; + + profile->framelist.push_back(frame); + + return 0; +} + +static lrtp_profile_t *get_next_profile(struct lrtp_t *lrtp) +{ + // TODO: This function /should/ return the next profile containing frame data, + // not just the next profile in the list (regardless of it's framequeue being + // empty!). + + if(lrtp->profiles.size() == 0) return NULL; // No profiles + + profile_map_t::iterator i = lrtp->profiles.find(lrtp->next_csrc); + + if(i == lrtp->profiles.end()) { + i = lrtp->profiles.begin(); + } + + struct lrtp_profile_t *profile = i->second; + + i++; + + if(i == lrtp->profiles.end()) { + i = lrtp->profiles.begin(); + } + + lrtp->next_csrc = i->second->csrc; + + return profile; +} + +EXPORT +int lrtp_pack(struct lrtp_t *lrtp, char *packet, size_t maxsize) +{ + //if(!profile) return PACK_MISSING_PROFILE; + + // TODO: Check profile magic word + + struct lrtp_profile_t *profile = get_next_profile(lrtp); + + if(profile == NULL || profile->framelist.size() == 0) return 0; + + inputframe_t *frame = profile->framelist.front(); + + RTP rtp; + rtp.setSSrc(lrtp->ssrc); + //rtp.setTimestamp(ts); // TODO... + rtp.setSeq(lrtp->seq); + rtp.addCSrc(profile->csrc); + + int ret = profile->pack(profile, + frame->data + frame->offset, + frame->size - frame->offset, + rtp); + + if(ret < 0) return ret; + + frame->offset += ret; + + if(frame->size == frame->offset) { + if(profile->process_finished) { + profile->process_finished(profile, frame->data, + profile->process_finished_ptr); + } + + profile->framelist.pop_front(); + delete frame; + } + + if(rtp.isValid()) { + size_t size = rtp.packet(packet, maxsize); + size = lrtp->srtp->encrypt(packet, size); + lrtp->seq++; + return size; + } + + return 0; +} + +EXPORT +int lrtp_dequeue_frame(struct lrtp_t *lrtp, + char *frame, size_t maxsize, + unsigned int *csrc, unsigned int *ts) +{ + if(lrtp->framelist.size() == 0) return 0; + + outputframe_t *f = lrtp->framelist.front(); + + if(f->size > maxsize) { + printf("Buffer is too small\n"); + return -1; // Buffer too small. + } + + memcpy(frame, f->data, f->size); + + lrtp->framelist.pop_front(); + + free(f->data); + delete f; + + return f->size; +} + +EXPORT +int lrtp_unpack(struct lrtp_t *lrtp, const char *packet, size_t size) +{ + if(!lrtp) return UNPACK_MISSING_HANDLE; + + // TODO: Check lrtp magic word + + char pkg[16*1024]; + memcpy(pkg, packet, size); + + size = lrtp->srtp->decrypt(pkg, size); + + RTP rtp; + rtp.fromPacket(pkg, size); + std::list csrcs = rtp.getCSrcs(); + if(csrcs.size() == 0) return UNPACK_MISSING_CSRC; + if(csrcs.size() > 1) return UNPACK_TOO_MANY_CSRCS; + + csrc_t csrc = *csrcs.begin(); + + struct lrtp_profile_t *profile = NULL; + if(lrtp->profiles.find(csrc) == lrtp->profiles.end()) { + return UNPACK_MISSING_PROFILE; + } + + profile = lrtp->profiles[csrc]; + + std::list framelist; + int ret = profile->unpack(profile, rtp, framelist); + lrtp->framelist.splice(lrtp->framelist.end(), framelist); + + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/lrtp.h b/src/lrtp.h index 7428997..ce6b0e2 100644 --- a/src/lrtp.h +++ b/src/lrtp.h @@ -27,4 +27,108 @@ */ #ifndef __LRTP_LRTP_H__ #define __LRTP_LRTP_H__ + +#ifdef WIN32 +#ifdef BUILD_DLL +/* DLL export */ +#define EXPORT __declspec(dllexport) +#else +/* EXE import */ +#define EXPORT __declspec(dllimport) +#endif +#else +#define EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lrtp_profiles.h" + +struct lrtp_t; + +EXPORT +struct lrtp_t *lrtp_init(const char *key, unsigned int ssrc); + +EXPORT +void lrtp_close(struct lrtp_t *lrtp); + +struct lrtp_profile_t; + +/** + * @param ... + */ +EXPORT +struct lrtp_profile_t *lrtp_create_profile(struct lrtp_t *lrtp, + lrtp_profile_id_t profile_id, + unsigned int csrc, ...); + +EXPORT +void lrtp_destroy_profile(struct lrtp_t *lrtp, unsigned int csrc); + +typedef enum { + // Errors: + PACK_ERROR = 1000, // Error... + PACK_BUFFER_TOO_SMALL = 1001, // Packet buffer needs to be bigger. + PACK_UNKNOWN_PROFILE = 1002, // Illegal profile id + PACK_MISSING_PROFILE = 1003, // Profile pointer NULL or not valid. +} lrtp_pack_status_t; + +/** + * Enqueue a media frame for the packetiser. + * @param profile A pointer to profile needed for processing the frame type. + * @param framedate The frame data that needs encapsulation. + * @param framesize The size in bytes of the frame data. + * @return 0 on success, or a negative error code on error. + * NOTE: The frame pointer cannot be freed or overwritten until all frame data + * has been handled by lrtp_pack(). Either call lrtp_pack() until it returns 0 + * after each call to lrtp_enqueue_frame or use the + * OPTION_SET_PROCESS_FINISHED_HANDLER option to set a process finished handler. + * See lrtp_profiles.h for further details. + */ +EXPORT +int lrtp_enqueue_frame(struct lrtp_profile_t *profile, + const char *framedate, size_t framesize); + +/** + * Handle frame data from the frame queue and create at most a single sRTP + * packet. + * @param lrtp The lrtp context handle. + * @param packet A char buffer which will contain the resulting RTP data. + * @param maxsize the maximum number of bytes that can be contained within + * packet. + * @return Returns the number of bytes written in packet, if any (can be zero + * if no packet is available) or a negative error code on error. + */ +EXPORT int lrtp_pack(struct lrtp_t *lrtp, char *packet, size_t maxsize); + +typedef enum { + // Errors: + UNPACK_ERROR = 1000, // Error... + UNPACK_BUFFER_TOO_SMALL = 1001, // Frame buffer needs to be bigger. + UNPACK_MISSING_HANDLE = 1002, // Handle pointer NULL or not valid. + UNPACK_MISSING_CSRC = 1003, // Exactly one csrc must be present. + UNPACK_TOO_MANY_CSRCS = 1004, // Exactly one csrc must be present. + UNPACK_MISSING_PROFILE = 1005, // CSrc from packet could not be connected with a preofile.. +} lrtp_unpack_status_t; + +/** + */ +EXPORT +int lrtp_dequeue_frame(struct lrtp_t *lrtp, + char *frame, size_t maxsize, + unsigned int *csrc, unsigned int *ts); + +/** + */ +EXPORT +int lrtp_unpack(struct lrtp_t *lrtp, const char *packet, size_t size); + +#ifdef __cplusplus +} +#endif + #endif/*__LRTP_LRTP_H__*/ diff --git a/src/lrtp_profiles.h b/src/lrtp_profiles.h new file mode 100644 index 0000000..a3d6db1 --- /dev/null +++ b/src/lrtp_profiles.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * lrtp_profiles.h + * + * Mon Sep 9 13:29:04 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_LRTP_PROFILES_H__ +#define __LRTP_LRTP_PROFILES_H__ + +// List of known RTP profiles: +typedef enum { + PROFILE_RAW = 0, // Dummy profile, for test + PROFILE_L16 = 1, + PROFILE_AMRWB = 2, + PROFILE_OPUS = 3, + PROFILE_JPEG = 4, +} lrtp_profile_id_t; + +// No more options. +#define OPTION_END 0 + +// Options relating to all profiles: +/** + * This option is used to set the custom processFinishedHandler + * It takes two arguments, the first being a function pointer of the type: + * void (*process_finished)(struct lrtp_profile_t *profile, + * const char *frame, size_t framesize, + * void *ptr); + * + * the second argument is the value of ptr, passed on to the callback function. + * See rtp_profile.h for further details. + */ +#define OPTION_SET_PROCESS_FINISHED_HANDLER 1000 + +// Raw profile options: +#define OPTION_RAW_PKG_SIZE 2000 // Integer argument. + // Number of bytes per rtp packet. + // Default is 100 + +// AMR-WB profile options: +#define OPTION_AMRWB_FRAME_TYPE_INDEX 3000 // Integer argument. + // Frame type index according to + // Table 1a in "3GPP TS 26.201" + // Default is 8: AMR-WB 23.85 kbit/s + +// Opus profile options: +// None + +// Jpeg profile options: + +#endif/*__LRTP_LRTP_PROFILES_H__*/ diff --git a/src/rtp.cc b/src/rtp.cc new file mode 100644 index 0000000..28a9203 --- /dev/null +++ b/src/rtp.cc @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp.cc + * + * Mon Sep 2 15:24:09 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "rtp.h" + +#include +#include +#include + +RTP::RTP() +{ + memset(&header, 0, sizeof(header)); + pads = 0; + + header.version = 2; + + memset(pload, 0, sizeof(pload)); + pload_size = 0; + + is_valid = false; +} + +void RTP::setPadding(unsigned char padding) +{ + if(padding == 0) { + header.p = 0; + pads = 0; + return; + } + header.p = 1; + pads = padding; +} + +unsigned char RTP::padding() const +{ + return pads; +} + +void RTP::setPayloadType(unsigned char pt) +{ + header.pt = pt; +} + +unsigned char RTP::payloadType() const +{ + return header.pt; +} + +void RTP::setMarker(bool bit) +{ + header.m = bit?1:0; +} + +bool RTP::marker() const +{ + return header.m; +} + +void RTP::setSeq(uint16_t seq) +{ + header.seq = htons(seq); +} + +uint16_t RTP::seq() const +{ + return ntohs(header.seq); +} + +void RTP::setTimestamp(uint32_t ts) +{ + header.ts = htonl(ts); +} + +uint32_t RTP::timestamp() const +{ + return ntohl(header.ts); +} + +void RTP::setSSrc(uint32_t ssrc) +{ + header.ssrc = htonl(ssrc); +} + +uint32_t RTP::SSrc() const +{ + return ntohl(header.ssrc); +} + +void RTP::addCSrc(csrc_t csrc) +{ + if(csrcs.size() == 15) { + printf("ERROR: Too many CSRCs (max 15).\n"); + return; // TODO: Report error! + } + csrcs.push_back(csrc); + header.cc = csrcs.size(); +} + +std::list RTP::getCSrcs() const +{ + return csrcs; +} + +void RTP::removeCSrc(csrc_t csrc) +{ + csrcs.remove(csrc); + header.cc = csrcs.size(); +} + +void RTP::clearCSrcs() +{ + csrcs.clear(); + header.cc = csrcs.size(); +} + +void RTP::setPayload(const char *pl, size_t size) +{ + if(size > MAX_RTP_PAYLOAD_SIZE) { + printf("ERROR: Payload is too big for RTP pakcet.\n"); + // TODO: Report error. + } + + memcpy(pload, pl, size); + pload_size = size; + + setValid(true); +} + +size_t RTP::payload(char *payload, size_t maxsize) const +{ + if(pload_size > maxsize) { + printf("ERROR: Payload is too big for the buffer.\n"); + return 0; // TODO: Report error. + } + + memcpy(payload, pload, pload_size); + return pload_size; +} + +size_t RTP::payloadSize() const +{ + return pload_size; +} + +const char *RTP::payloadData() const +{ + return pload; +} + +size_t RTP::packet(char *packet, size_t maxsize) const +{ + memset(packet, 0, maxsize); + + size_t total_packet_size = + sizeof(header) + sizeof(csrc_t) * csrcs.size() + pload_size + pads; + + if(total_packet_size > maxsize) { + printf("ERROR: Packet is too big for the buffer.\n"); + return 0; // TODO: Report error. + } + + char *p = packet; + + // Copy header fields. + memcpy(p, &header, sizeof(header)); + + p += sizeof(header); + + // Write CSrcs to packet + csrc_t *pcsrc = (csrc_t*)p; + std::list::const_iterator i = csrcs.begin(); + while(i != csrcs.end()) { + *pcsrc++ = htonl(*i); + i++; + } + + p += sizeof(csrc_t) * csrcs.size(); + + // TODO: Insert header extension here (if it exists) + + // Write payload to body. + memcpy(p, pload, pload_size); + + p += pload_size; + + if(pads) { + for(int i = 0; i < pads - 1; i++) *p++ = '\0'; + *p++ = (unsigned char)pads; + } + + return p - packet; +} + +void RTP::fromPacket(const char *packet, size_t size) +{ + rtp_header_t *hdr = (rtp_header_t *)packet; + + if(hdr->version != 2) { + printf("ERROR: Wrong RTP header version.\n"); + return; // TODO: Invalid RTP version error + } + + setMarker(hdr->m); + setPayloadType(hdr->pt); + setSeq(ntohs(hdr->seq)); + setTimestamp(ntohl(hdr->ts)); + setSSrc(ntohl(hdr->ssrc)); + + if(hdr->p) { + setPadding(*(unsigned char*)&packet[size - 1]); + } + + // TODO: Use header extension flag: hdr->x + + // Read out CSrc's: + csrc_t *pcsrc = (csrc_t*)(packet + sizeof(rtp_header_t)); + for(int i = 0; i < hdr->cc; i++) { + addCSrc(ntohl(*pcsrc)); + pcsrc++; + } + + // Read out payload + size_t plsz = size - (sizeof(rtp_header_t) + hdr->cc*sizeof(csrc_t) + pads); + char *pl = (char*)pcsrc; + setPayload(pl, plsz); + + setValid(true); +} + +bool RTP::isValid() +{ + return is_valid; +} + +void RTP::setValid(bool valid) +{ + is_valid = valid; +} diff --git a/src/rtp.h b/src/rtp.h new file mode 100644 index 0000000..b7aa8b4 --- /dev/null +++ b/src/rtp.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp.h + * + * Mon Sep 2 15:24:09 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_RTP_H__ +#define __LRTP_RTP_H__ + +#include +#include +#include + +typedef struct { + uint16_t cc:4; // CSRC count + uint16_t x:1; // Header extension flag + uint16_t p:1; // Padding flag + uint16_t version:2; // Protocol version + + uint16_t m:1; // Marker bit + uint16_t pt:7; // Payload type + + uint16_t seq; // Sequence number + + uint32_t ts; // Timestamp + uint32_t ssrc; // Synchronization source +} rtp_header_t; + +#define MAX_RTP_PACKET_SIZE (16 * 1024) +#define MAX_RTP_PAYLOAD_SIZE ( MAX_RTP_PACKET_SIZE - sizeof(rtp_header_t) ) + +typedef struct { + rtp_header_t header; + char body[MAX_RTP_PAYLOAD_SIZE]; +} rtp_t; + +typedef unsigned int csrc_t; + +class RTP { +public: + RTP(); + + /* // Not implemented + void setHeaderExtension(); + int headerExtension(); + */ + + void setPadding(unsigned char padding); + unsigned char padding() const; + + void setPayloadType(unsigned char pt); + unsigned char payloadType() const; + + void setMarker(bool bit); + bool marker() const; + + void setSeq(uint16_t seq); + uint16_t seq() const; + + void setTimestamp(uint32_t seq); + uint32_t timestamp() const; + + void setSSrc(uint32_t ssrc); + uint32_t SSrc() const; + + void addCSrc(csrc_t csrc); + std::list getCSrcs() const; + void removeCSrc(csrc_t csrc); + void clearCSrcs(); + + void setPayload(const char *payload, size_t size); + size_t payload(char *payload, size_t maxsize) const; + size_t payloadSize() const; + const char *payloadData() const; + + size_t packet(char *packet, size_t maxsize) const; + void fromPacket(const char *packet, size_t size); + + bool isValid(); + void setValid(bool valid); + +private: + std::list csrcs; + + rtp_header_t header; + + unsigned char pads; + + char pload[MAX_RTP_PAYLOAD_SIZE]; + size_t pload_size; + + bool is_valid; +}; + +#endif/*__LRTP_RTP_H__*/ diff --git a/src/rtp_profile.h b/src/rtp_profile.h new file mode 100644 index 0000000..96dc9eb --- /dev/null +++ b/src/rtp_profile.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile.h + * + * Wed Sep 11 08:40:11 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_RTP_PROFILE_H__ +#define __LRTP_RTP_PROFILE_H__ + +#include +#include + +#include "rtp.h" + +struct lrtp_t; + +typedef struct { + const char *data; + size_t size; + size_t offset; +} inputframe_t; + +typedef struct { + char *data; + size_t size; + + csrc_t csrc; + unsigned int ts; + // TODO: Add other metadata ... + +} outputframe_t; + +struct lrtp_profile_t { + struct lrtp_t *lrtp; + lrtp_profile_id_t id; + unsigned int csrc; + + // Frames ready for packing with this profile: + std::list framelist; + + // Profile functions: + /** + * Custom callback used when a frame has been processed. + * This could be the place to call free(3), decrease a reference counter or + * re-add to a memory pool. + * Use ptr to pass custom parameters to the callback. + * If NULL, no call is performed. + */ + void (*process_finished)(struct lrtp_profile_t *profile, + const char *frame, void *ptr); + void *process_finished_ptr; + + int (*pack)(struct lrtp_profile_t *profile, + const char *frame, size_t framesize, + RTP &rtp); + + int (*unpack)(struct lrtp_profile_t *profile, + const RTP &rtp, + std::list &framelist); + + void (*destroy)(struct lrtp_profile_t *profile); + +}; + +#endif/*__LRTP_RTP_PROFILE_H__*/ diff --git a/src/rtp_profile_amrwb.cc b/src/rtp_profile_amrwb.cc new file mode 100644 index 0000000..f83b3c3 --- /dev/null +++ b/src/rtp_profile_amrwb.cc @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile_amrwb.cc + * + * Wed Oct 2 14:43:05 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "rtp_profile_amrwb.h" + +#include "rtp_profile.h" +#include "rtp.h" + +#include +#include + +/* +The timestamp clock frequency is the same as the sampling frequency, so the +timestamp unit is in samples. + +The duration of one speech frame-block is 20 ms for both AMR and AMR-WB. For +AMR, the sampling frequency is 8 kHz, corresponding to 160 encoded speech +samples per frame from each channel. For AMR-WB, the sampling frequency is +16 kHz, corresponding to 320 samples per frame from each channel. Thus, the +timestamp is increased by 160 for AMR and 320 for AMR-WB for each consecutive +frame-block. + +QUESTION: Should we use 16kHz or 19351Hz? +ANSWER: The encoder and decoder respectively should use 19531Hz, the rest of +the system can assume 16kHz without loss of quality. + */ + +struct lrtp_profile_amrwb_t { + struct lrtp_profile_t profile; // 'Inherit' lrtp_profile_t + + int frame_type_index; +}; + +// Frame sizes based on frame type index: +static const int wb_frame_size[16] = { + 17, 23, 32, 36, 40, 46, 50, 58, + 60, 5, -1, -1, -1, -1, -1, 0 +}; + +/* + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |F| FT |Q|P|P| + * +-+-+-+-+-+-+-+-+ + */ + +/* + * F (1 bit): If set to 1, indicates that this frame is followed by + * another speech frame in this payload; if set to 0, indicates that + * this frame is the last frame in this payload. + */ +static int is_last_frame(const char *frame_header) +{ + return ( (*frame_header >> 7) & 0x1 ) == 0; +} + +static void set_is_last_frame(char *frame_header, int last) +{ + last = 1 - last; // Invert 'last': 0 means last, 1 means more + *frame_header = (*frame_header & 0x7f) | ( (last & 0x1) << 7); +} + +/* + * FT (4 bits): Frame type index, indicating either the AMR or AMR-WB + * speech coding mode or comfort noise (SID) mode of the + * corresponding frame carried in this payload. + */ +static int frame_type_index(const char *frame_header) +{ + return (*frame_header >> 3) & 0xf; +} + +static int set_frame_type_index(char *frame_header, int index) +{ + *frame_header = (*frame_header & 0x87) | ( (index & 0xf) << 3); +} + +/* + * Q (1 bit): Frame quality indicator. If set to 0, indicates the + * corresponding frame is severely damaged and the receiver should + * set the RX_TYPE (see [6]) to either SPEECH_BAD or SID_BAD + * depending on the frame type (FT). + */ +static int quality_indicator(const char *frame_header) +{ + return (*frame_header >> 2) & 0x1; +} + +static int set_quality_indicator(char *frame_header, int quality) +{ + *frame_header = (*frame_header & 0xf8) | ( (quality & 0x1) << 2); +} + + +// http://tools.ietf.org/html/rfc3267#section-4.4 +int profile_amrwb_pack(struct lrtp_profile_t *profile, + const char *input, size_t inputsize, + RTP &rtp) +{ + // NOTE: This implementation does not support frame interleaving... + + struct lrtp_profile_amrwb_t *p = (struct lrtp_profile_amrwb_t *)profile; + + size_t frameheader_size = 1; + size_t cmr_size = 1; + + // Frame size based on frame_type_index, + size_t frame_size = wb_frame_size[p->frame_type_index]; + + // This is the maximum number of frames we have room for in a single RTP + // payload. + size_t max_num_frames = + (MAX_RTP_PAYLOAD_SIZE - cmr_size) / (frame_size + frameheader_size); + + size_t cpsz = inputsize; + + printf("cpsz: %d\n", cpsz); + + size_t num_frames = cpsz / frame_size; + + if(num_frames == 0) { + printf("Garbage at the end of the buffer\n"); + rtp.setValid(false); + return -1; + } + + if(num_frames > max_num_frames) num_frames = max_num_frames; + + char *payload = (char*)malloc(MAX_RTP_PAYLOAD_SIZE); + + /* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+- - - - - - - - + * | CMR |R|R|R|R| ILL | ILP | + * +-+-+-+-+-+-+-+-+- - - - - - - - + */ + // If a terminal has no preference in which mode to receive, it SHOULD set + // CMR=15 in all its outbound payloads. + char *CMR = payload; + *CMR = 15; + + // The ILL and ILP fields are not used (see note above). + + char *frame_header = CMR + cmr_size; + for(size_t i = 0; i < num_frames; i++) { + // 0 if this is the last frame. + set_is_last_frame(frame_header, i == (num_frames - 1) ); + + // Frame type index (4 bits): + set_frame_type_index(frame_header, p->frame_type_index); + + // Frame quality indicator (1 bit): Set to 0 to indicate damaged frame. + set_quality_indicator(frame_header, 1); + + frame_header += frameheader_size; + } + + char *frame_data = frame_header; + + size_t payload_size = cmr_size; + const char *frame_offset = input; + for(size_t i = 0; i < num_frames; i++) { + memcpy(frame_data, frame_offset, frame_size); + frame_data += frame_size; + frame_offset += frame_size; + payload_size += frameheader_size + frame_size; + } + + //size_t payload_size = cmr_size + num_frames*(frame_size + frameheader_size); + rtp.setPayload(payload, payload_size); + rtp.setValid(true); + + printf("payload (pack):\n"); + for(int i = 0; i < payload_size; i++) { + printf("%02x ", (unsigned char)payload[i]); + } + printf("\n"); + + free(payload); + + return cpsz; +} + +int profile_amrwb_unpack(struct lrtp_profile_t *profile, + const RTP &rtp, + std::list &framelist) +{ + struct lrtp_profile_amrwb_t *p = (struct lrtp_profile_amrwb_t *)profile; + + size_t size = rtp.payloadSize(); + const char *payload = rtp.payloadData(); + + printf("---------------------------------------- New packet:\n"); + printf("payload (unpack):\n"); + for(int i = 0; i < size; i++) { + printf("%02x ", (unsigned char)payload[i]); + } + printf("\n"); + + // Read CMR: + size_t cmr_size = 1; + char CMR = payload[0]; + if(CMR != 15) { + printf("CMR not 15\n"); + return UNPACK_ERROR; + } + + // TODO: What to do if there are 0 frames (and therefore no header to set the + // 'last frame' bit in....) + + // Read ToC: + size_t frameheader_size = 1; + const char *frame_toc; + frame_toc = payload + cmr_size; + + size_t frame_num = 0; + const char *frame_data = frame_toc; + while(!is_last_frame(frame_data)) { + frame_num++; + frame_data += frameheader_size; + } + frame_num++; + frame_data += frameheader_size; + + while(frame_num) { + size_t frame_size_idx = frame_type_index(frame_toc); + size_t frame_size = wb_frame_size[frame_size_idx]; + + outputframe_t *of = new outputframe_t(); + of->size = frame_size; + char *buf = (char*)malloc(of->size); + memcpy(buf, frame_data, frame_size); + of->data = buf; + + framelist.push_back(of); + + if(is_last_frame(frame_toc)) return 0; + + frame_toc += frameheader_size; + frame_data += frame_size; + + frame_num--; + } + + printf("Error, missed the last_frame bit\n"); + + return 1; // Error, missed the last_frame bit +} + +void profile_amrwb_destroy(struct lrtp_profile_t *profile) +{ + struct lrtp_profile_amrwb_t *p = (struct lrtp_profile_amrwb_t *)profile; + delete p; +} + +struct lrtp_profile_t *profile_amrwb_create(struct lrtp_t *lrtp, + unsigned int csrc, + va_list vp) +{ + struct lrtp_profile_amrwb_t *p = new struct lrtp_profile_amrwb_t; + + p->profile.pack = profile_amrwb_pack; + p->profile.unpack = profile_amrwb_unpack; + p->profile.destroy = profile_amrwb_destroy; + + p->frame_type_index = 8; // Default: AMR-WB 23.85 kbit/s + + while(true) { + int type = va_arg(vp, int); + if(type == OPTION_END) break; + + switch(type) { + case OPTION_AMRWB_FRAME_TYPE_INDEX: + p->frame_type_index = va_arg(vp, int); + break; + default: + // TODO: Unknown arg type + break; + } + } + + return (struct lrtp_profile_t *)p; +} diff --git a/src/rtp_profile_amrwb.h b/src/rtp_profile_amrwb.h new file mode 100644 index 0000000..38b20c1 --- /dev/null +++ b/src/rtp_profile_amrwb.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile_amrwb.h + * + * Wed Oct 2 14:43:05 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_RTP_PROFILE_AMRWB_H__ +#define __LRTP_RTP_PROFILE_AMRWB_H__ + +#include + +#include "lrtp.h" + +struct lrtp_profile_t *profile_amrwb_create(struct lrtp_t *lrtp, + unsigned int csrc, + va_list ap); + +#endif/*__LRTP_RTP_PROFILE_AMRWB_H__*/ diff --git a/src/rtp_profile_opus.cc b/src/rtp_profile_opus.cc new file mode 100644 index 0000000..102d175 --- /dev/null +++ b/src/rtp_profile_opus.cc @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile_opus.cc + * + * Tue Sep 10 13:53:24 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Part of this code is an adaptation of the GStreamer RTP profile code for Celt + * by Wim Taymans + * The original code can be found in the gst-plugins-good package version 1.1.3: + * http://gstreamer.freedesktop.org/src/gst-plugins-good + */ +#include "rtp_profile_opus.h" + +/* + +--------------+----------------+-----------+----------+ + | Abbreviation | Name | Bandwidth | Sampling | + +--------------+----------------+-----------+----------+ + | nb | Narrowband | 0 - 4000 | 8000 | + | | | | | + | mb | Mediumband | 0 - 6000 | 12000 | + | | | | | + | wb | Wideband | 0 - 8000 | 16000 | + | | | | | + | swb | Super-wideband | 0 - 12000 | 24000 | + | | | | | + | fb | Fullband | 0 - 20000 | 48000 | + +--------------+----------------+-----------+----------+ + + Audio bandwidth naming + + Table 1 +*/ +/* +Recommended Bitrate + + For a frame size of 20 ms, these are the bitrate "sweet spots" for + Opus in various configurations: + o 8-12 kb/s for NB speech, + o 16-20 kb/s for WB speech, + o 28-40 kb/s for FB speech, + o 48-64 kb/s for FB mono music, and + o 64-128 kb/s for FB stereo music. +*/ + +/* +Forward Error Correction (FEC) + + The voice mode of Opus allows for "in-band" forward error correction + (FEC) data to be embedded into the bit stream of Opus. +*/ + +/* +RTP Header Usage + + The format of the RTP header is specified in [RFC3550]. The Opus + payload format uses the fields of the RTP header consistent with this + specification. + + The payload length of Opus is a multiple number of octets and + therefore no padding is required. The payload MAY be padded by an + integer number of octets according to [RFC3550]. + + The marker bit (M) of the RTP header is used in accordance with + Section 4.1 of [RFC3551]. + + The RTP payload type for Opus has not been assigned statically and is + expected to be assigned dynamically. + + The receiving side MUST be prepared to receive duplicates of RTP + packets. Only one of those payloads MUST be provided to the Opus + decoder for decoding and others MUST be discarded. + + Opus supports 5 different audio bandwidths which may be adjusted + during the duration of a call. The RTP timestamp clock frequency is + defined as the highest supported sampling frequency of Opus, i.e. + 48000 Hz, for all modes and sampling rates of Opus. The unit for the + timestamp is samples per single (mono) channel. The RTP timestamp + corresponds to the sample time of the first encoded sample in the + encoded frame. For sampling rates lower than 48000 Hz the number of + samples has to be multiplied with a multiplier according to Table 2 + to determine the RTP timestamp. + + +---------+------------+ + | fs (Hz) | Multiplier | + +---------+------------+ + | 8000 | 6 | + | | | + | 12000 | 4 | + | | | + | 16000 | 3 | + | | | + | 24000 | 2 | + | | | + | 48000 | 1 | + +---------+------------+ + + Table 2: Timestamp multiplier +*/ + +#include "rtp_profile.h" + +#include +#include + +struct lrtp_profile_opus_t { + struct lrtp_profile_t profile; // 'Inherit' lrtp_profile_t +}; + +int profile_opus_pack(struct lrtp_profile_t *profile, + const char *frame, size_t framesize, + RTP &rtp) +{ + //struct lrtp_profile_opus_t *p = (struct lrtp_profile_opus_t *)profile; + (void)profile; + + rtp.setPayload(frame, framesize); + rtp.setValid(true); + + return framesize; +} + +int profile_opus_unpack(struct lrtp_profile_t *profile, + const RTP &rtp, + std::list &framelist) +{ + struct lrtp_profile_opus_t *p = (struct lrtp_profile_opus_t *)profile; + + outputframe_t *of = new outputframe_t(); + of->size = rtp.payloadSize(); + char *buf = (char*)malloc(of->size); + of->size = rtp.payload(buf, of->size); + of->data = buf; + + framelist.push_back(of); + + return 0; +} + +void profile_opus_destroy(struct lrtp_profile_t *profile) +{ + struct lrtp_profile_opus_t *p = (struct lrtp_profile_opus_t *)profile; + delete p; +} + +struct lrtp_profile_t *profile_opus_create(struct lrtp_t *lrtp, + unsigned int csrc, + va_list vp) +{ + struct lrtp_profile_opus_t *p = new struct lrtp_profile_opus_t; + + p->profile.pack = profile_opus_pack; + p->profile.unpack = profile_opus_unpack; + p->profile.destroy = profile_opus_destroy; + + while(true) { + int type = va_arg(vp, int); + if(type == OPTION_END) break; + + switch(type) { + default: + // TODO: Unknown arg type + break; + } + } + + return &p->profile; +} diff --git a/src/rtp_profile_opus.h b/src/rtp_profile_opus.h new file mode 100644 index 0000000..23002d3 --- /dev/null +++ b/src/rtp_profile_opus.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile_opus.h + * + * Tue Sep 10 13:53:24 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_RTP_PROFILE_OPUS_H__ +#define __LRTP_RTP_PROFILE_OPUS_H__ + +#include + +#include "lrtp.h" + +struct lrtp_profile_t *profile_opus_create(struct lrtp_t *lrtp, + unsigned int csrc, + va_list ap); + +#endif/*__LRTP_RTP_PROFILE_OPUS_H__*/ diff --git a/src/rtp_profile_raw.cc b/src/rtp_profile_raw.cc new file mode 100644 index 0000000..cf34186 --- /dev/null +++ b/src/rtp_profile_raw.cc @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile_raw.cc + * + * Mon Sep 2 14:30:56 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "rtp_profile_raw.h" + +#include "rtp_profile.h" + +#include +#include + +struct lrtp_profile_raw_t { + struct lrtp_profile_t profile; // 'Inherit' lrtp_profile_t + + unsigned short int pkg_size; + + // Cache + char *pkg_cache; + int pkg_cache_size; +}; + +int profile_raw_pack(struct lrtp_profile_t *profile, + const char *frame, size_t framesize, + RTP &rtp) +{ + struct lrtp_profile_raw_t *p = (struct lrtp_profile_raw_t *)profile; + + size_t cpsz = p->pkg_size - p->pkg_cache_size; + + if(cpsz > framesize) cpsz = framesize; + + if(cpsz != 0) { + memcpy(p->pkg_cache, frame, cpsz); + p->pkg_cache_size += cpsz; + } + + if(p->pkg_cache_size == p->pkg_size) { + rtp.setPayload(p->pkg_cache, p->pkg_cache_size); + p->pkg_cache_size = 0; + } + + return cpsz; +} + +int profile_raw_unpack(struct lrtp_profile_t *profile, + const RTP &rtp, + std::list &framelist) +{ + struct lrtp_profile_raw_t *p = (struct lrtp_profile_raw_t *)profile; + + outputframe_t *of = new outputframe_t(); + of->size = rtp.payloadSize(); + char *buf = (char*)malloc(of->size); + of->size = rtp.payload(buf, of->size); + of->data = buf; + + framelist.push_back(of); + + return 0; +} + +void profile_raw_destroy(struct lrtp_profile_t *profile) +{ + struct lrtp_profile_raw_t *p = (struct lrtp_profile_raw_t *)profile; + delete p->pkg_cache; + delete p; +} + +struct lrtp_profile_t *profile_raw_create(struct lrtp_t *lrtp, + unsigned int csrc, + va_list vp) +{ + struct lrtp_profile_raw_t *p = new struct lrtp_profile_raw_t; + + p->profile.pack = profile_raw_pack; + p->profile.unpack = profile_raw_unpack; + p->profile.destroy = profile_raw_destroy; + + p->pkg_size = 100; + + while(true) { + int type = va_arg(vp, int); + if(type == OPTION_END) break; + + switch(type) { + case OPTION_RAW_PKG_SIZE: + p->pkg_size = va_arg(vp, int); + break; + default: + // TODO: Unknown arg type + break; + } + } + + p->pkg_cache = new char[p->pkg_size]; + p->pkg_cache_size = 0; + + return &p->profile; +} + diff --git a/src/rtp_profile_raw.h b/src/rtp_profile_raw.h new file mode 100644 index 0000000..febae5d --- /dev/null +++ b/src/rtp_profile_raw.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * rtp_profile_raw.h + * + * Mon Sep 2 14:30:56 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_RTP_PROFILE_RAW_H__ +#define __LRTP_RTP_PROFILE_RAW_H__ + +#include + +#include "lrtp.h" + +struct lrtp_profile_t *profile_raw_create(struct lrtp_t *lrtp, + unsigned int csrc, + va_list ap); + +#endif/*__LRTP_RTP_PROFILE_RAW_H__*/ diff --git a/src/srtp.cc b/src/srtp.cc new file mode 100644 index 0000000..98c4002 --- /dev/null +++ b/src/srtp.cc @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * srtp.cc + * + * Thu Sep 5 10:46:10 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "srtp.h" + +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif + +#include "asc2bin.h" + +struct SRTP::prv { + char *key; + size_t key_len; + + unsigned int ssrc; + + srtp_t session; + srtp_policy_t policy; +}; + +static bool is_initialised = false; + +SRTP::SRTP(std::string key, unsigned int ssrc) +{ + prv = NULL; + + err_status_t status; + + if(!is_initialised) { + status = srtp_init(); + if(status != err_status_ok) { + // TODO: Error handling + printf("srtp_init failed %d\n", status); + } + is_initialised = true; + } + + prv = new struct prv; + + prv->ssrc = ssrc; + + setupKey(key); + setupPolicy(true, true); + + status = srtp_create(&prv->session, &prv->policy); + if(status != err_status_ok) { + // TODO: Error handling + printf("srtp_create %d\n", status); + } + + status = srtp_add_stream(prv->session, &prv->policy); + if(status != err_status_ok) { + // TODO: Error handling + printf("srtp_add_stream %d\n", status); + } +} + +SRTP::~SRTP() +{ + err_status_t status = srtp_remove_stream(prv->session, htonl(prv->ssrc)); + if(status != err_status_ok) { + // TODO: Error handling + printf("srtp_remove_stream failed %d\n", status); + } + + status = srtp_dealloc(prv->session); + if(status != err_status_ok) { + // TODO: Error handling + printf("srtp_dealloc failed %d\n", status); + } + + status = srtp_shutdown(); + if(status != err_status_ok) { + // TODO: Error handling + printf("srtp_shutdown failed %d\n", status); + } + + is_initialised = false; + + if(prv) { + free(prv->key); + delete prv; + } +} + +void SRTP::setupKey(const std::string &key) +{ + prv->key = (char *)calloc(MASTER_KEY_LEN, 1); + prv->key_len = MASTER_KEY_LEN; + + if(key.length() > MASTER_KEY_LEN * 2) printf("KeyTooLong\n"); // TODO + + // Read key from hexadecimal on command line into an octet string + ssize_t len = asc2bin(prv->key, prv->key_len, key.c_str(), key.size()); + + if(len == -1) printf("InvalidHexKeyString\n"); // TODO + prv->key_len = len; + + // check that hex string is the right length. + if(len < MASTER_KEY_LEN) printf("KeyTooShort\n"); // TODO +} + +void SRTP::setupPolicy(bool confidentiality, bool authentication) +{ +#ifndef USE_CRYPTO + confidentiality = authentication = false; + printf("No crypto!\n") +#endif + + /* + * create policy structure, using the default mechanisms but + * with only the security services requested on the command line, + * using the right SSRC value + */ + if(confidentiality && authentication) { + crypto_policy_set_aes_cm_128_hmac_sha1_80(&prv->policy.rtp); + prv->policy.rtp.sec_serv = sec_serv_conf_and_auth; + } else if(confidentiality && !authentication) { + crypto_policy_set_aes_cm_128_null_auth(&prv->policy.rtp); + prv->policy.rtp.sec_serv = sec_serv_conf; + } else if(!confidentiality && authentication) { + crypto_policy_set_null_cipher_hmac_sha1_80(&prv->policy.rtp); + prv->policy.rtp.sec_serv = sec_serv_auth; + } else { + /* + * we're not providing security services, so set the policy to the + * null policy + * + * Note that this policy does not conform to the SRTP + * specification, since RTCP authentication is required. However, + * the effect of this policy is to turn off SRTP, so that this + * application is now a vanilla-flavored RTP application. + */ + prv->policy.rtp.cipher_type = NULL_CIPHER; + prv->policy.rtp.cipher_key_len = 0; + prv->policy.rtp.auth_type = NULL_AUTH; + prv->policy.rtp.auth_key_len = 0; + prv->policy.rtp.auth_tag_len = 0; + prv->policy.rtp.sec_serv = sec_serv_none; + } + + crypto_policy_set_rtcp_default(&prv->policy.rtcp); + prv->policy.rtcp.sec_serv = sec_serv_none; // No need for RTCP + + prv->policy.key = (uint8_t *)prv->key; + prv->policy.ssrc.type = ssrc_specific; + prv->policy.ssrc.value = prv->ssrc; + prv->policy.next = NULL; +} + +int SRTP::encrypt(char *packet, size_t size) +{ + int sz = size; + err_status_t status = srtp_protect(prv->session, packet, &sz); + if(status != err_status_ok) { + // TODO: throw SRTP::UnprotectException(); + printf("srtp_protect failed %d\n", status); + } + + return sz; +} + +int SRTP::decrypt(char *packet, size_t size) +{ + int sz = size; + err_status_t status = srtp_unprotect(prv->session, packet, &sz); + switch(status) { + case err_status_ok: + // No errors. + break; + case err_status_replay_fail: + // TODO: throw SRTP::ReplayException();// (replay check failed) + printf("srtp_unprotect failed replay %d\n", status); + break; + case err_status_replay_old: + // TODO: throw SRTP::ReplayOldException();// (replay check failed) + printf("srtp_unprotect failed replay_old %d\n", status); + break; + case err_status_auth_fail: + // TODO: throw SRTP::AuthCheckException();// (auth check failed) + printf("srtp_unprotect failed auth %d\n", status); + break; + default: + // TODO: throw SRTP::UnprotectException(); + printf("srtp_unprotect failed %d\n", status); + break; + } + + /* + if(octets_recvd - RTP_HEADER_LEN > (ssize_t)size) { + printf("BufferSize %d\n", status); + } + */ + + // TODO: rtp.fromBuffer(packet, size); + //memcpy(buf, prv->receiver->message.body, octets_recvd - RTP_HEADER_LEN); + + return sz; +} diff --git a/src/srtp.h b/src/srtp.h new file mode 100644 index 0000000..3d8b697 --- /dev/null +++ b/src/srtp.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * srtp.h + * + * Thu Sep 5 10:46:10 CEST 2013 + * Copyright 2013 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of lrtp. + * + * lrtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * lrtp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with lrtp; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __LRTP_SRTP_H__ +#define __LRTP_SRTP_H__ + +#include + +#include "rtp.h" + +#define MAX_KEY_LEN 64 +#define MASTER_KEY_LEN 30 + +class SRTP { +public: + SRTP(std::string key, unsigned int ssrc); + ~SRTP(); + + int encrypt(char *packet, size_t size); + int decrypt(char *packet, size_t size); + +private: + struct prv; + struct prv *prv; + + void setupKey(const std::string &key); + void setupPolicy(bool confidentiality, bool authentication); +}; + +#endif/*__LRTP_SRTP_H__*/ -- cgit v1.2.3