summaryrefslogtreecommitdiffstats
path: root/tinyDAV/src/codecs/amr/tdav_codec_amr.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV/src/codecs/amr/tdav_codec_amr.c')
-rw-r--r--tinyDAV/src/codecs/amr/tdav_codec_amr.c816
1 files changed, 816 insertions, 0 deletions
diff --git a/tinyDAV/src/codecs/amr/tdav_codec_amr.c b/tinyDAV/src/codecs/amr/tdav_codec_amr.c
new file mode 100644
index 0000000..9304f85
--- /dev/null
+++ b/tinyDAV/src/codecs/amr/tdav_codec_amr.c
@@ -0,0 +1,816 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tdav_codec_amr.c
+ * @brief AMR-NB and AMR-WB codecs.
+ * RTP payloader/depayloader are based on RFC 4867
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/amr/tdav_codec_amr.h"
+
+#include "tsk_params.h"
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <stdlib.h> /* atoi() */
+
+#if HAVE_OPENCORE_AMR
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "..\\thirdparties\\win32\\lib\\opencore\\libopencore-amrnb.a")
+#endif
+
+#define NO_DATA 15
+#define DEFAULT_ENC_MODE ((enum Mode)MR122) /* Higher, could be changed by remote party by using CMR */
+
+/* From WmfDecBytesPerFrame in dec_input_format_tab.cpp */
+static const int tdav_codec_amr_nb_sizes[] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0 };
+/* From pvamrwbdecoder_api.h, by dividing by 8 and rounding up */
+static const int tdav_codec_amr_wb_sizes[] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, -1, -1, -1, -1, -1, -1 };
+
+/* ============ Common ================= */
+static int tdav_codec_amr_init(tdav_codec_amr_t* self, tdav_codec_amr_type_t type, tdav_codec_amr_mode_t mode);
+static int tdav_codec_amr_deinit(tdav_codec_amr_t* self);
+static tdav_codec_amr_mode_t tdav_codec_amr_get_mode(const char* fmtp);
+static int tdav_codec_amr_parse_fmtp(tdav_codec_amr_t* self, const char* fmtp);
+static tsk_size_t tdav_codec_amr_oa_decode(tdav_codec_amr_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr);
+static tsk_size_t tdav_codec_amr_be_decode(tdav_codec_amr_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr);
+static tsk_size_t tdav_codec_amr_be_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size);
+static tsk_size_t tdav_codec_amr_oa_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size);
+static uint8_t tdav_codec_amr_bitbuffer_read(const void* bits, tsk_size_t size, tsk_size_t start, tsk_size_t count);
+
+/* ============ AMR-NB Plugin interface =================
+ The AMR codec was originally developed and standardized by the
+ European Telecommunications Standards Institute (ETSI) for GSM
+ cellular systems. It is now chosen by the Third Generation
+ Partnership Project (3GPP) as the mandatory codec for third
+ generation (3G) cellular systems [1].
+
+ The AMR codec is a multi-mode codec that supports eight narrow band
+ speech encoding modes with bit rates between 4.75 and 12.2 kbps. The
+ sampling frequency used in AMR is 8000 Hz and the speech encoding is
+ performed on 20 ms speech frames. Therefore, each encoded AMR speech
+ frame represents 160 samples of the original speech.
+
+ Among the eight AMR encoding modes, three are already separately
+ adopted as standards of their own. Particularly, the 6.7 kbps mode
+ is adopted as PDC-EFR [18], the 7.4 kbps mode as IS-641 codec in TDMA
+ [17], and the 12.2 kbps mode as GSM-EFR [16].
+*/
+
+int tdav_codec_amrnb_open(tmedia_codec_t* self)
+{
+ tdav_codec_amr_t* amrnb = (tdav_codec_amr_t*)self;
+
+ if(!TDAV_CODEC_AMR(amrnb)->encoder){
+ if(!(TDAV_CODEC_AMR(amrnb)->encoder = Encoder_Interface_init(0))){
+ TSK_DEBUG_ERROR("Failed to initialize AMR-NB encoder");
+ return -2;
+ }
+ }
+
+ if(!TDAV_CODEC_AMR(amrnb)->decoder){
+ if(!(TDAV_CODEC_AMR(amrnb)->decoder = Decoder_Interface_init())){
+ TSK_DEBUG_ERROR("Failed to initialize AMR-NB encoder");
+ return -2;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_codec_amrnb_close(tmedia_codec_t* self)
+{
+ tdav_codec_amr_t* amrnb = (tdav_codec_amr_t*)self;
+
+ if(TDAV_CODEC_AMR(amrnb)->encoder){
+ Encoder_Interface_exit(TDAV_CODEC_AMR(amrnb)->encoder);
+ TDAV_CODEC_AMR(amrnb)->encoder = tsk_null;
+ }
+
+ if(TDAV_CODEC_AMR(amrnb)->decoder){
+ Decoder_Interface_exit(TDAV_CODEC_AMR(amrnb)->decoder);
+ TDAV_CODEC_AMR(amrnb)->decoder = tsk_null;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_amrnb_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tdav_codec_amr_t* amr = (tdav_codec_amr_t*)self;
+
+ switch(amr->mode){
+ case tdav_codec_amr_mode_be:
+ return tdav_codec_amr_be_encode(amr, in_data, in_size, out_data, out_max_size);
+ default:
+ return tdav_codec_amr_oa_encode(amr, in_data, in_size, out_data, out_max_size);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_amrnb_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tdav_codec_amr_t* amr = (tdav_codec_amr_t*)self;
+
+ switch(amr->mode){
+ case tdav_codec_amr_mode_be:
+ return tdav_codec_amr_be_decode(amr, in_data, in_size, out_data, out_max_size, proto_hdr);
+ default:
+ return tdav_codec_amr_oa_decode(amr, in_data, in_size, out_data, out_max_size, proto_hdr);
+ }
+}
+
+char* tdav_codec_amrnb_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ const tdav_codec_amr_t* amr = (const tdav_codec_amr_t*)codec;
+
+ /* We support all modes, all ... */
+ if(amr){
+ switch(amr->mode){
+ case tdav_codec_amr_mode_be:
+ return tsk_strdup("octet-align=0");
+ default:
+ return tsk_strdup("octet-align=1");
+ }
+ }
+ return tsk_null;
+}
+
+tsk_bool_t tdav_codec_amrnb_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ tdav_codec_amr_t* amr;
+ if(!(amr = (tdav_codec_amr_t*)codec) || !att_name){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ if(amr && tsk_striequals(att_name, "fmtp")){
+ /* Match mode */
+ if(tdav_codec_amr_get_mode(att_value) != amr->mode){
+ TSK_DEBUG_INFO("Failed to match [%s]", att_value);
+ return tsk_false;
+ }
+ /* check parameters validity */
+ if(tdav_codec_amr_parse_fmtp(amr, att_value)){
+ TSK_DEBUG_INFO("Failed to match [%s]", att_value);
+ return tsk_false;
+ }
+
+ return tsk_true;
+ }
+ return tsk_false;
+}
+
+
+//
+// AMR-NB OA Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_amrnb_oa_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_amr_t *amrnb_oa = self;
+ if(amrnb_oa){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_amr_init(TDAV_CODEC_AMR(amrnb_oa), tdav_codec_amr_type_nb, tdav_codec_amr_mode_oa);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_amrnb_oa_dtor(tsk_object_t * self)
+{
+ tdav_codec_amr_t *amrnb_oa = self;
+ if(amrnb_oa){
+ /* deinit base */
+ tmedia_codec_audio_deinit(amrnb_oa);
+ /* deinit self */
+ tdav_codec_amr_deinit(TDAV_CODEC_AMR(amrnb_oa));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_amrnb_oa_def_s =
+{
+ sizeof(tdav_codec_amr_t),
+ tdav_codec_amrnb_oa_ctor,
+ tdav_codec_amrnb_oa_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_amrnb_oa_plugin_def_s =
+{
+ &tdav_codec_amrnb_oa_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_amr_nb_oa,
+ "AMR",
+ "AMR Narrow Band - Octet Aligned (libopencore-amr)",
+ TMEDIA_CODEC_FORMAT_AMR_NB_OA,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_amrnb_open,
+ tdav_codec_amrnb_close,
+ tdav_codec_amrnb_encode,
+ tdav_codec_amrnb_decode,
+ tdav_codec_amrnb_sdp_att_match,
+ tdav_codec_amrnb_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_amrnb_oa_plugin_def_t = &tdav_codec_amrnb_oa_plugin_def_s;
+
+//
+// AMR-NB BE Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_amrnb_be_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_amr_t *amrnb_be = self;
+ if(amrnb_be){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_amr_init(TDAV_CODEC_AMR(amrnb_be), tdav_codec_amr_type_nb, tdav_codec_amr_mode_be);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_amrnb_be_dtor(tsk_object_t * self)
+{
+ tdav_codec_amr_t *amrnb_be = self;
+ if(amrnb_be){
+ /* deinit base */
+ tmedia_codec_audio_deinit(amrnb_be);
+ /* deinit self */
+ tdav_codec_amr_deinit(TDAV_CODEC_AMR(amrnb_be));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_amrnb_be_def_s =
+{
+ sizeof(tdav_codec_amr_t),
+ tdav_codec_amrnb_be_ctor,
+ tdav_codec_amrnb_be_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_amrnb_be_plugin_def_s =
+{
+ &tdav_codec_amrnb_be_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_amr_nb_be,
+ "AMR",
+ "AMR Narrow Band - Bandwidth-Efficient (libopencore-amr)",
+ TMEDIA_CODEC_FORMAT_AMR_NB_BE,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_amrnb_open,
+ tdav_codec_amrnb_close,
+ tdav_codec_amrnb_encode,
+ tdav_codec_amrnb_decode,
+ tdav_codec_amrnb_sdp_att_match,
+ tdav_codec_amrnb_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_amrnb_be_plugin_def_t = &tdav_codec_amrnb_be_plugin_def_s;
+
+
+
+
+
+
+
+
+
+//
+// Common functions
+//
+
+static int tdav_codec_amr_init(tdav_codec_amr_t* self, tdav_codec_amr_type_t type, tdav_codec_amr_mode_t mode)
+{
+ if(self){
+ self->type = type;
+ self->mode = mode;
+ self->encoder_mode = DEFAULT_ENC_MODE;
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+}
+
+static int tdav_codec_amr_deinit(tdav_codec_amr_t* self)
+{
+ if(self){
+ switch(self->type){
+ case tdav_codec_amr_type_nb:
+ { /* AMR-NB */
+ if(self->encoder){
+ Encoder_Interface_exit(self->encoder);
+ self->encoder = tsk_null;
+ }
+ if(self->decoder){
+ Decoder_Interface_exit(self->decoder);
+ self->decoder = tsk_null;
+ }
+ break;
+ }
+ case tdav_codec_amr_type_wb:
+ { /* AMR-WB */
+ break;
+ }
+ }
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+}
+
+static tdav_codec_amr_mode_t tdav_codec_amr_get_mode(const char* fmtp)
+{
+ /* RFC 4867 - 8.1. AMR Media Type Registration
+ octet-align: Permissible values are 0 and 1. If 1, octet-aligned
+ operation SHALL be used. If 0 or if not present, bandwidth-efficient operation is employed.
+ */
+ tdav_codec_amr_mode_t mode = tdav_codec_amr_mode_be;
+ tsk_size_t size = tsk_strlen(fmtp);
+ int start, end;
+
+ if((start = tsk_strindexOf(fmtp, size, "octet-align")) !=-1){
+ tsk_param_t* param;
+ if((end = tsk_strindexOf((fmtp+start), (size-start), ";")) == -1){
+ end = size;
+ }
+ if((param = tsk_params_parse_param((fmtp+start), (end-start)))){
+ if(param->value && tsk_strequals(param->value, "1")){
+ mode = tdav_codec_amr_mode_oa;
+ }
+ TSK_OBJECT_SAFE_FREE(param);
+ }
+ }
+ return mode;
+}
+
+int tdav_codec_amr_parse_fmtp(tdav_codec_amr_t* self, const char* fmtp)
+{
+ int ret = 0;
+ int val_int;
+ const char* val_str;
+ //--tdav_codec_amr_mode_t mode = self->mode;
+ tsk_params_L_t* params = tsk_null;
+
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ /* Do not check "octet-align" => already done by the caller of this function */
+
+ /* === mode-set ===*/
+ if((val_str = tsk_params_get_param_value(params, "mode-set"))){
+ char* modes = tsk_strdup(val_str);
+ char *pch, *saveptr;
+ int mode_int;
+ pch = tsk_strtok_r(modes, ", ", &saveptr);
+ while(pch){
+ mode_int = atoi(pch);
+ self->modes |= 0x0001 << mode_int;
+ pch = tsk_strtok_r(tsk_null, ", ", &saveptr);
+ }
+
+ TSK_FREE(modes);
+ }
+ else{
+ self->modes = 0xFFFF;
+ }
+
+ /* === interleaving ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "interleaving")) != -1){
+ TSK_DEBUG_WARN("interleaving not supported");
+ ret = -1; goto bail;
+ }
+ /* === mode-change-period ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "mode-change-period")) != -1){
+ if(val_int != 1 && val_int != 2){
+ TSK_DEBUG_ERROR("Invalid [mode-change-period]");
+ ret = -1; goto bail;
+ }
+ self->mcp = (unsigned)val_int;
+ }
+ /* === mode-change-capability ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "mode-change-capability")) != -1){
+ if(val_int != 1 && val_int != 2){
+ TSK_DEBUG_ERROR("Invalid [mode-change-capability]");
+ ret = -1; goto bail;
+ }
+ self->mcc = (unsigned)val_int;
+ }
+ /* === mode-change-neighbor ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "mode-change-neighbor")) != -1){
+ if(val_int != 0 && val_int != 1){
+ TSK_DEBUG_ERROR("Invalid [mode-change-neighbor]");
+ ret = -1; goto bail;
+ }
+ self->mcn = (unsigned)val_int;
+ }
+ }
+
+bail:
+ TSK_OBJECT_SAFE_FREE(params);
+ return ret;
+}
+
+
+/* RFC 4867 - 4.2. Payload Structure
+ +----------------+-------------------+----------------
+ | payload header | table of contents | speech data ...
+ +----------------+-------------------+----------------
+*/
+/* RFC 4867 - 4.4.2. The Payload Table of Contents and Frame CRCs
+ The table of contents (ToC) consists of a list of ToC entries, each representing a speech frame.
+ +---------------------+
+ | list of ToC entries |
+ +---------------------+
+ | list of frame CRCs | (optional)
+ - - - - - - - - - - -
+ Note, for ToC entries with FT=14 or 15, there will be no
+ corresponding speech frame or frame CRC present in the payload.
+*/
+
+
+static tsk_size_t tdav_codec_amr_be_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size = 0, i;
+ int ret_size;
+ uint8_t ToC;
+ static uint8_t CMR = NO_DATA /* No interleaving */;
+
+ uint8_t outbuf[60 + 1]; /* enought for both NB and WB at ptime=20ms */
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_be)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Encode */
+ if((ret_size = Encoder_Interface_Encode(amr->encoder, amr->encoder_mode, in_data, outbuf, 0)) <= 0){
+ TSK_DEBUG_ERROR("Encoder_Interface_Encode() failed");
+ goto bail;
+ }
+
+
+ /* allocate output buffer */
+ if((int)*out_max_size <ret_size){
+ if(!(*out_data = tsk_realloc(*out_data, ret_size))){
+ *out_max_size = 0;
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ goto bail;
+ }
+ *out_max_size = ret_size;
+ }
+
+ out_size = ret_size;
+
+ /* CMR (4bits) */
+ ((uint8_t*)*out_data)[0] = (CMR<<4);
+ /* ToC (Always ONE Frame, don't need to test for [F]) (6bits)*/
+ ToC = outbuf[0]>>2/*2*[P]*/;
+ ((uint8_t*)*out_data)[0] |= (ToC >> 2/*[Q],[1-FT]*/) & 0xF; /* 4bits */
+ ((uint8_t*)*out_data)[1] = (ToC & 0x3/*[1-FT],[Q]*/)<<6; /* 2bits */
+
+ /* === THERE ARE 2 EXTRA BITS === */
+
+ for(i=1; i<out_size-1; i++){
+ ((uint8_t*)*out_data)[i] |= outbuf[i]>>2;/* 6bits */
+ ((uint8_t*)*out_data)[i+1] = outbuf[i]<<6;/* 2bits */
+ }
+
+bail:
+ return out_size;
+}
+
+tsk_size_t tdav_codec_amr_be_decode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size = 0, pcm_frame_size = 0, index = 0;
+ const uint8_t* pdata = (const uint8_t*)in_data;
+ //--const uint8_t* pend = (pdata + in_size);
+ uint8_t CMR;
+ int toc_entries = 0, i, k; // ToC entries count
+
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_be)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* compute PCM frame size */
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ pcm_frame_size = 160 * sizeof(short);
+ break;
+ case tdav_codec_amr_type_wb:
+ pcm_frame_size = 320 * sizeof(short);
+ break;
+ default:
+ TSK_DEBUG_ERROR("Invalid AMR type");
+ return 0;
+ }
+
+ /* CMR (4bits) */
+ CMR = tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), index, 4);
+ index += 4;
+ if(CMR != NO_DATA){
+ amr->encoder_mode = (enum Mode)CMR;
+ }
+
+ /* F(1bit), FT(4bits), Q(1bit) */
+ /* count ToC entries */
+ do{ /* At least ONE ToC */
+ ++toc_entries;
+ ++pdata;
+ index += 6;
+ }
+ while((index < (in_size*8)) && (tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), (index-6), 1)/* F */));
+
+ for(i = 0; (i<toc_entries && (in_size < (in_size*8))) ; i++){
+ int size = -1;
+ uint8_t* speech_data = tsk_null;
+ //--int speech_data_size = 0;
+ uint8_t ToC = tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), 4/*CMR*/ + (i*6), 6);
+
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ size = tdav_codec_amr_nb_sizes[(ToC>>1)&0x0F/* FT */];
+ break;
+ case tdav_codec_amr_type_wb:
+ size = tdav_codec_amr_wb_sizes[(ToC>>1)&0x0F/* FT */];
+ break;
+ }
+
+ if((speech_data = tsk_calloc((size + 2/* ToC + '\0' */), sizeof(uint8_t)))){
+ /* copy ToC */
+ speech_data[0] = (ToC & 0x1F)<<2/* 2*[P] */; /* ToC as OA layout */
+ /* copy speech data */
+ for(k=0; k<size; k++){
+ speech_data[1 + k] = tdav_codec_amr_bitbuffer_read(in_data, (in_size*8), index, 8);
+ index+=8;
+ if((k==size-1) && (index%8)){
+ speech_data[1 + k] <<= (8-(index%8)); //clean
+ }
+ }
+
+ /* allocate/reallocate speech data */
+ if(*out_max_size <(out_size + pcm_frame_size)){
+ if(!(*out_data = tsk_realloc(*out_data, (out_size + pcm_frame_size)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ TSK_FREE(speech_data);
+ goto bail;
+ }
+ *out_max_size = out_size + pcm_frame_size;
+ }
+
+ /* decode speech data */
+ Decoder_Interface_Decode(amr->decoder, speech_data, &((short*)*out_data)[out_size/sizeof(short)], 0);
+ out_size += pcm_frame_size, pdata+= size;
+
+ TSK_FREE(speech_data);
+ }
+ }
+
+bail:
+ return out_size;
+}
+
+static tsk_size_t tdav_codec_amr_oa_encode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size = 0;
+ int ret_size;
+ static uint8_t CMR = NO_DATA /* No interleaving */;
+
+ uint8_t outbuf[60 + 1]; /* enought for both NB and WB at ptime=20ms */
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_oa)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Encode */
+ if((ret_size = Encoder_Interface_Encode(amr->encoder, amr->encoder_mode, in_data, outbuf, 0)) <= 0){
+ TSK_DEBUG_ERROR("Encoder_Interface_Encode() failed");
+ goto bail;
+ }
+
+ out_size = ret_size + 1 /* CMR without interleaving */;
+ /* allocate output buffer */
+ if(*out_max_size <out_size){
+ if(!(*out_data = tsk_realloc(*out_data, out_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = out_size = 0;
+ goto bail;
+ }
+ *out_max_size = out_size;
+ }
+
+ /* CMR */
+ ((uint8_t*)*out_data)[0] = (CMR << 4);
+ /* Only ONE ToC --> believe me */
+ memcpy(&((uint8_t*)*out_data)[1], outbuf, ret_size);
+
+bail:
+ return out_size;
+}
+
+static tsk_size_t tdav_codec_amr_oa_decode(tdav_codec_amr_t* amr, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ tsk_size_t out_size = 0, pcm_frame_size = 0;
+ const uint8_t* pdata = (const uint8_t*)in_data;
+ const uint8_t* pend = (pdata + in_size);
+ uint8_t CMR;
+ int toc_entries = 0, i; // ToC entries count
+
+ if(!amr || !in_data || !in_size || !out_data || (amr->mode != tdav_codec_amr_mode_oa)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* compute PCM frame size */
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ pcm_frame_size = 160 * sizeof(short);
+ break;
+ case tdav_codec_amr_type_wb:
+ pcm_frame_size = 320 * sizeof(short);
+ break;
+ default:
+ TSK_DEBUG_ERROR("Invalid AMR type");
+ return 0;
+ }
+
+ /* RFC 4867 - 4.4. Octet-Aligned Mode
+ In octet-aligned mode, the payload header consists of a 4-bit CMR, 4
+ reserved bits, and optionally, an 8-bit interleaving header, as shown
+ below:
+
+ 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 |
+ +-+-+-+-+-+-+-+-+- - - - - - - -
+
+ CMR (4 bits): same as defined in Section 4.3.1.
+
+ "interleaving" not supported ==> could ignore ILL and ILP (wich are optional)
+ */
+
+ CMR = (*pdata++ >> 4);
+ if(CMR != NO_DATA){
+ /* The codec mode request received in the CMR field is valid until the
+ next codec mode request is received, i.e., a newly received CMR value
+ corresponding to a speech mode, or NO_DATA overrides the previously
+ received CMR value corresponding to a speech mode or NO_DATA. */
+ amr->encoder_mode = (enum Mode)CMR; // As we support all modes, do not check for validity
+ }
+
+ /*
+ A ToC entry takes the following format in octet-aligned mode:
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |F| FT |Q|P|P|
+ +-+-+-+-+-+-+-+-+
+
+ F (1 bit): see definition in Section 4.3.2.
+ FT (4 bits, unsigned integer): see definition in Section 4.3.2.
+ Q (1 bit): see definition in Section 4.3.2.
+ P bits: padding bits, MUST be set to zero, and MUST be ignored on reception.
+ */
+
+ /* count ToC entries */
+ do{ /* At least ONE ToC */
+ ++toc_entries;
+ ++pdata;
+ }
+ while(pdata && (pdata < pend) && (pdata[-1] >> 7/* F */));
+
+ for(i = 0; (i<toc_entries && (pdata < pend)) ; i++){
+ int size = -1;
+ uint8_t* speech_data = tsk_null;
+ //--int speech_data_size = 0;
+ uint8_t ToC = ((const uint8_t*)in_data)[1/*CMR...*/ + i];
+ switch(TDAV_CODEC_AMR(amr)->type){
+ case tdav_codec_amr_type_nb:
+ size = tdav_codec_amr_nb_sizes[(ToC>>3) & 0x0F/* FT */];
+ break;
+ case tdav_codec_amr_type_wb:
+ size = tdav_codec_amr_wb_sizes[(ToC>>3) & 0x0F/* FT */];
+ break;
+ }
+
+ /* check size */
+ if(size <0 || ((pdata + size) > pend)){
+ TSK_DEBUG_ERROR("Invalid size");
+ break;
+ }
+
+ if((speech_data = tsk_calloc((size + 2/* ToC + '\0' */), sizeof(uint8_t)))){
+ /* copy ToC */
+ *speech_data = ToC & 0x7F/* with 'F'=0 */;
+ /* copy speech data */
+ memcpy((speech_data + 1), pdata, size);
+ /* allocate/reallocate speech data */
+ if(*out_max_size <(out_size + pcm_frame_size)){
+ if(!(*out_data = tsk_realloc(*out_data, (out_size + pcm_frame_size)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ TSK_FREE(speech_data);
+ goto bail;
+ }
+ *out_max_size = (out_size + pcm_frame_size);
+ }
+ /* decode speech data */
+ Decoder_Interface_Decode(amr->decoder, speech_data, &((short*)*out_data)[out_size/sizeof(short)], 0);
+ out_size += pcm_frame_size, pdata+= size;
+
+ TSK_FREE(speech_data);
+ }
+ }
+
+bail:
+ return out_size;
+}
+
+
+static uint8_t tdav_codec_amr_bitbuffer_read(const void* bits, tsk_size_t size, tsk_size_t start, tsk_size_t count)
+{
+ uint8_t byte, left, right, pad;
+
+ if(!bits || !size || count>8){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if((start + count) > size){
+ count = (size - start);
+ }
+
+ pad = start ? (8 - (start % 8)) : count;
+ left = ((uint8_t*)bits)[start/8] << (8-pad);
+ right = ((uint8_t*)bits)[((start+count)<size ? (start+count) : start)/8] >> pad;
+
+ if((start && (start % 8) != ((start+count)%8)) || (!start && count>8)){
+ /* overlap */
+ byte = (left | right) >> (8-count);
+ }
+ else{
+ byte = (left | right) & (0xFF >> (8-count));
+ }
+
+ return byte;
+}
+
+#endif /* HAVE_OPENCORE_AMR */
OpenPOWER on IntegriCloud