diff options
Diffstat (limited to 'src/rtp_profile_amrwb.cc')
-rw-r--r-- | src/rtp_profile_amrwb.cc | 307 |
1 files changed, 307 insertions, 0 deletions
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 <stdio.h> +#include <string.h> + +/* +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<outputframe_t *> &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; +} |