/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set et sw=2 ts=2: */ /*************************************************************************** * lrtp.cc * * Wed Aug 28 09:50:55 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 General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lrtp; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "lrtp.h" #include #include #include #include #include "rtp_profile.h" #include "rtp_profile_amrwb.h" #include "rtp_profile_opus.h" #include "rtp_profile_raw.h" #include "rtp_profile_l16.h" #include "rtp_profile_jpeg.h" #include "srtp.h" #ifdef __cplusplus extern "C" { #endif #define MAGIC 0x726c7074 // Check if 'h' is a valid smtl handle and report/return error if not. #define CHECK_HANDLE(h) \ do { \ if(!h || h->magic != MAGIC) { \ return LRTP_MISSING_HANDLE; \ } \ } while(0) typedef std::map profile_map_t; struct lrtp_t { unsigned int magic; SRTP *srtp; csrc_t ssrc; unsigned short seq; profile_map_t profiles; csrc_t next_csrc; // for frame iterator (see get_next_frame) oframelist_t 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 }, { PROFILE_L16, profile_raw_create }, { PROFILE_JPEG, profile_jpeg_create }, { } }; EXPORT struct lrtp_t *lrtp_init(enum lrtp_status_t *status, const char *key, unsigned int ssrc) { if(status == NULL) return NULL; *status = LRTP_OK; struct lrtp_t *lrtp = new (std::nothrow) struct lrtp_t; if(!lrtp) { *status = LRTP_OUT_OF_MEMORY; return NULL; } try { lrtp->srtp = new SRTP(key, ssrc); } catch(enum lrtp_status_t s) { *status = s; delete lrtp; return NULL; } lrtp->ssrc = ssrc; lrtp->seq = 0; lrtp->magic = MAGIC; return lrtp; } EXPORT enum lrtp_status_t lrtp_close(struct lrtp_t *lrtp) { CHECK_HANDLE(lrtp); enum lrtp_status_t status = LRTP_OK; try { if(lrtp->srtp) delete lrtp->srtp; lrtp->srtp = NULL; } catch(enum lrtp_status_t s) { status = s; } lrtp->magic = 0; try { delete lrtp; } catch(enum lrtp_status_t s) { status = s; } return status; } EXPORT enum lrtp_status_t lrtp_create_profile(struct lrtp_t *lrtp, lrtp_profile_id_t profile_id, unsigned int csrc, ...) { CHECK_HANDLE(lrtp); struct lrtp_profile_t *profile = NULL; if(lrtp->profiles.find(csrc) != lrtp->profiles.end()) { return LRTP_CSRC_ALREADY_ACTIVE; } 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) { return LRTP_MISSING_PROFILE; } profile->id = profile_id; profile->lrtp = lrtp; profile->csrc = csrc; lrtp->profiles[csrc] = profile; return LRTP_OK; } EXPORT enum lrtp_status_t lrtp_destroy_profile(struct lrtp_t *lrtp, unsigned int csrc) { CHECK_HANDLE(lrtp); if(lrtp->profiles.find(csrc) == lrtp->profiles.end()) { return LRTP_MISSING_CSRC; } struct lrtp_profile_t *profile = lrtp->profiles[csrc]; profile->destroy(profile); lrtp->profiles.erase(csrc); return LRTP_OK; } EXPORT enum lrtp_status_t lrtp_enqueue_frame(struct lrtp_t *lrtp, unsigned int csrc, char *data, size_t size, unsigned long int timestamp, int flags) { CHECK_HANDLE(lrtp); if(lrtp->profiles.find(csrc) == lrtp->profiles.end()) { return LRTP_MISSING_CSRC; } struct lrtp_profile_t *profile = lrtp->profiles[csrc]; inputframe_t *frame = new inputframe_t(); if((flags & LRTP_TAKE_OWNERSHIP) == LRTP_TAKE_OWNERSHIP) { frame->owned = true; frame->data = data; } else if((flags & LRTP_COPY) == LRTP_COPY) { frame->owned = true; frame->data = (char*)malloc(size); memcpy(frame->data, data, size); } else { frame->owned = false; frame->data = data; } frame->size = size; frame->offset = 0; frame->timestamp = timestamp; //printf("lrtp_enqueue_frame: frame->size: %d\n", frame->size); profile->framelist.push_back(frame); return LRTP_OK; } // Assume we have at least one csrc in the list static csrc_t get_next_csrc(struct lrtp_t *lrtp) { profile_map_t::iterator i = lrtp->profiles.find(lrtp->next_csrc); if(i == lrtp->profiles.end()) { // If we haven't got a valid csrc start from the beginning. i = lrtp->profiles.begin(); } else { // Otherwise increase the iterator, potentially wrapping. i++; if(i == lrtp->profiles.end()) i = lrtp->profiles.begin(); } lrtp->next_csrc = i->second->csrc; return lrtp->next_csrc; } static lrtp_profile_t *get_next_profile(struct lrtp_t *lrtp) { if(lrtp->profiles.size() == 0) return NULL; // No profiles csrc_t start = get_next_csrc(lrtp); csrc_t cur = start; do { if(lrtp->profiles.find(cur) == lrtp->profiles.end()) { printf("ERROR: This shouldn't happen." " Selected profile is not in the list\n"); break; } struct lrtp_profile_t *profile = lrtp->profiles.find(cur)->second; if(profile->framelist.size()) return profile; cur = get_next_csrc(lrtp); } while(cur != start); return NULL; } EXPORT int lrtp_pack(struct lrtp_t *lrtp, char *packet, size_t maxsize) { CHECK_HANDLE(lrtp); 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(frame->timestamp); 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(); if(frame->owned) free(frame->data); 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) { CHECK_HANDLE(lrtp); 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. } size_t framesize = f->size; memcpy(frame, f->data, f->size); if(csrc) *csrc = f->csrc; if(ts) *ts = f->ts; lrtp->framelist.pop_front(); //printf("%d frames left in framelist.\n", lrtp->framelist.size()); free(f->data); delete f; return framesize; } EXPORT enum lrtp_status_t lrtp_unpack(struct lrtp_t *lrtp, const char *packet, size_t size) { CHECK_HANDLE(lrtp); char *pkg = (char*)malloc(size); memcpy(pkg, packet, size); int ret = 0; try { ret = lrtp->srtp->decrypt(pkg, size); size = ret; } catch(enum lrtp_status_t s) { free(pkg); return s; } RTP rtp; rtp.fromPacket(pkg, size); std::list csrcs = rtp.getCSrcs(); if(csrcs.size() == 0) { free(pkg); return LRTP_MISSING_CSRC; } if(csrcs.size() > 1) { free(pkg); return LRTP_TOO_MANY_CSRCS; } csrc_t csrc = *csrcs.begin(); struct lrtp_profile_t *profile = NULL; if(lrtp->profiles.find(csrc) == lrtp->profiles.end()) { free(pkg); return LRTP_MISSING_PROFILE; } profile = lrtp->profiles[csrc]; oframelist_t framelist; ret = profile->unpack(profile, rtp, framelist); if(ret < 0) { printf("lrtp_unpack::Profile unpack failed: %d\n", ret); free(pkg); return (enum lrtp_status_t)ret; } // Make sure all frames in the list have the correct csrc. oframelist_t::iterator fi = framelist.begin(); while(fi != framelist.end()) { (*fi)->csrc = csrc; fi++; } lrtp->framelist.splice(lrtp->framelist.end(), framelist); free(pkg); return LRTP_OK; } // NOTE: lrtp_strerror implemented in error.cc #ifdef __cplusplus } #endif