summaryrefslogtreecommitdiffstats
path: root/tinyDAV/src/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV/src/codecs')
-rw-r--r--tinyDAV/src/codecs/amr/tdav_codec_amr.c816
-rw-r--r--tinyDAV/src/codecs/bfcp/tdav_codec_bfcp.c104
-rw-r--r--tinyDAV/src/codecs/bv/tdav_codec_bv16.c250
-rw-r--r--tinyDAV/src/codecs/bv/tdav_codec_bv32.c0
-rw-r--r--tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c126
-rw-r--r--tinyDAV/src/codecs/fec/tdav_codec_red.c263
-rw-r--r--tinyDAV/src/codecs/fec/tdav_codec_ulpfec.c424
-rw-r--r--tinyDAV/src/codecs/g711/g711.c295
-rw-r--r--tinyDAV/src/codecs/g711/tdav_codec_g711.c326
-rw-r--r--tinyDAV/src/codecs/g722/g722_decode.c400
-rw-r--r--tinyDAV/src/codecs/g722/g722_encode.c426
-rw-r--r--tinyDAV/src/codecs/g722/tdav_codec_g722.c219
-rw-r--r--tinyDAV/src/codecs/g729/tdav_codec_g729.c466
-rw-r--r--tinyDAV/src/codecs/gsm/tdav_codec_gsm.c209
-rw-r--r--tinyDAV/src/codecs/h261/tdav_codec_h261.c536
-rw-r--r--tinyDAV/src/codecs/h263/tdav_codec_h263.c1373
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264.c993
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264_cisco.cxx882
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264_cuda.cxx1130
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx2221
-rw-r--r--tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c411
-rw-r--r--tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c265
-rw-r--r--tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c818
-rw-r--r--tinyDAV/src/codecs/msrp/tdav_codec_msrp.c106
-rw-r--r--tinyDAV/src/codecs/opus/tdav_codec_opus.c363
-rw-r--r--tinyDAV/src/codecs/speex/tdav_codec_speex.c286
-rw-r--r--tinyDAV/src/codecs/t140/tdav_codec_t140.c175
-rw-r--r--tinyDAV/src/codecs/theora/tdav_codec_theora.c862
-rw-r--r--tinyDAV/src/codecs/vpx/tdav_codec_vp8.c1059
29 files changed, 15804 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 */
diff --git a/tinyDAV/src/codecs/bfcp/tdav_codec_bfcp.c b/tinyDAV/src/codecs/bfcp/tdav_codec_bfcp.c
new file mode 100644
index 0000000..3495295
--- /dev/null
+++ b/tinyDAV/src/codecs/bfcp/tdav_codec_bfcp.c
@@ -0,0 +1,104 @@
+/*
+* Copyright (C) 2014 Mamadou DIOP.
+*
+* 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_bfcp.c
+ * @brief The The Binary Floor Control Protocol (BFCP, rfc4582) session.
+ */
+#include "tinydav/codecs/bfcp/tdav_codec_bfcp.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ BFCP Plugin interface ================= */
+#define tdav_codec_bfcp_open tsk_null
+#define tdav_codec_bfcp_close tsk_null
+#define tdav_codec_bfcp_sdp_att_get tsk_null
+#define tdav_codec_bfcp_sdp_att_get tsk_null
+#define tdav_codec_bfcp_encode tsk_null
+#define tdav_codec_bfcp_decode tsk_null
+
+static tsk_bool_t tdav_codec_bfcp_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// BFCP Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_bfcp_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_bfcp_t *bfcp = self;
+ if (bfcp) {
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_bfcp_dtor(tsk_object_t * self)
+{
+ tdav_codec_bfcp_t *bfcp = self;
+ if (bfcp) {
+ /* deinit base */
+ tmedia_codec_bfcp_deinit(bfcp);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_bfcp_def_s =
+{
+ sizeof(tdav_codec_bfcp_t),
+ tdav_codec_bfcp_ctor,
+ tdav_codec_bfcp_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_bfcp_plugin_def_s =
+{
+ &tdav_codec_bfcp_def_s,
+
+ tmedia_bfcp,
+ tmedia_codec_id_none, // fake codec without real id
+ "application",
+ "BFCP fake codec",
+ TMEDIA_CODEC_FORMAT_BFCP,
+ tsk_false,
+ 0, // rate
+
+ /* audio */
+ {0},
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_bfcp_open,
+ tdav_codec_bfcp_close,
+ tdav_codec_bfcp_encode,
+ tdav_codec_bfcp_decode,
+ tdav_codec_bfcp_sdp_att_match,
+ tdav_codec_bfcp_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_bfcp_plugin_def_t = &tdav_codec_bfcp_plugin_def_s;
diff --git a/tinyDAV/src/codecs/bv/tdav_codec_bv16.c b/tinyDAV/src/codecs/bv/tdav_codec_bv16.c
new file mode 100644
index 0000000..21850fb
--- /dev/null
+++ b/tinyDAV/src/codecs/bv/tdav_codec_bv16.c
@@ -0,0 +1,250 @@
+/*
+* 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_bv16.c
+ * @brief BroadVoice16 codec
+ * The payloader/depayloader follow RFC 4298
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/bv/tdav_codec_bv16.h"
+
+#if HAVE_BV16
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "..\\thirdparties\\win32\\lib\\BroadVoice16\\libbv16.a")
+#endif
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include "typedef.h"
+#include "bvcommon.h"
+#include "bv16cnst.h"
+#include "bv16strct.h"
+#include "bv16.h"
+#include "utility.h"
+#if G192BITSTREAM
+#include "g192.h"
+#else
+#include "bitpack.h"
+#endif
+#include "memutil.h"
+
+/* RFC 4298 - 3.1. BroadVoice16 Bit Stream Definition */
+#define TDAV_BV16_FRAME_SIZE 10
+#define FRSZ_IN_U8 (FRSZ*2)
+
+/* ============ BV16 Plugin interface ================= */
+
+#define tdav_codec_bv16_sdp_att_get tsk_null
+#define tdav_codec_bv16_fmtp_set tsk_null
+
+static int sizestate = sizeof(struct BV16_Encoder_State);
+static int sizebitstream = sizeof(struct BV16_Bit_Stream);
+
+int tdav_codec_bv16_open(tmedia_codec_t* self)
+{
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+
+ if(!bv16->encoder.state){
+ bv16->encoder.state = allocWord16(0, sizestate/2-1);
+ Reset_BV16_Encoder((struct BV16_Encoder_State*)bv16->encoder.state);
+ }
+ if(!bv16->encoder.bs){
+ bv16->encoder.bs = allocWord16(0, sizebitstream/2-1);
+ }
+ if(!bv16->encoder.x){
+ bv16->encoder.x = allocWord16(0, FRSZ-1);
+ }
+
+ if(!bv16->decoder.state){
+ bv16->decoder.state = allocWord16(0, sizestate/2-1);
+ Reset_BV16_Decoder((struct BV16_Decoder_State*)bv16->decoder.state);
+ }
+ if(!bv16->decoder.bs){
+ bv16->decoder.bs = allocWord16(0, sizebitstream/2-1);
+ }
+ if(!bv16->decoder.x){
+ bv16->decoder.x = allocWord16(0, FRSZ-1);
+ }
+
+ return 0;
+}
+
+int tdav_codec_bv16_close(tmedia_codec_t* self)
+{
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+
+ if(bv16->encoder.state){
+ deallocWord16(bv16->encoder.state, 0, sizestate/2-1);
+ bv16->encoder.state = tsk_null;
+ }
+ if(bv16->encoder.bs){
+ deallocWord16(bv16->encoder.bs, 0, sizebitstream/2-1);
+ bv16->encoder.bs = tsk_null;
+ }
+ if(bv16->encoder.x){
+ deallocWord16(bv16->encoder.x, 0, FRSZ-1);
+ bv16->encoder.x = tsk_null;
+ }
+
+ if(bv16->decoder.state){
+ deallocWord16(bv16->decoder.state, 0, sizestate/2-1);
+ bv16->decoder.state = tsk_null;
+ }
+ if(bv16->encoder.bs){
+ deallocWord16(bv16->decoder.bs, 0, sizebitstream/2-1);
+ bv16->decoder.bs = tsk_null;
+ }
+ if(bv16->decoder.x){
+ deallocWord16(bv16->decoder.x, 0, FRSZ-1);
+ bv16->decoder.x = tsk_null;
+ }
+
+
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_bv16_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ //tsk_size_t out_size = 0;
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_bv16_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)
+{
+ tsk_size_t out_size = 0;
+ int i;
+ tdav_codec_bv16_t* bv16 = (tdav_codec_bv16_t*)self;
+ uint8_t mama[600];
+
+ if(!self || !in_data || !in_size || !out_data || (in_size % TDAV_BV16_FRAME_SIZE)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ for(i=0; i<(int)in_size; i+=TDAV_BV16_FRAME_SIZE){
+ BV16_BitUnPack(mama, (struct BV16_Bit_Stream*)bv16->decoder.bs);
+ //BV16_BitUnPack(&((UWord8 *)in_data)[i], (struct BV16_Bit_Stream*)bv16->decoder.bs);
+ BV16_Decode((struct BV16_Bit_Stream*)bv16->decoder.bs, (struct BV16_Decoder_State*)bv16->decoder.state, bv16->decoder.x);
+
+
+ if(*out_max_size<(out_size + FRSZ_IN_U8)){
+ if(!(*out_data = tsk_realloc(*out_data, (out_size + FRSZ_IN_U8)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = (out_size + FRSZ_IN_U8);
+ }
+ memcpy(&((uint8_t*)* out_data)[out_size], bv16->decoder.x, FRSZ_IN_U8);
+ out_size += FRSZ_IN_U8;
+ }
+
+
+ return out_size;
+}
+
+tsk_bool_t tdav_codec_bv16_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// BV16 Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_bv16_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_bv16_t *bv16 = self;
+ if(bv16){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_bv16_dtor(tsk_object_t * self)
+{
+ tdav_codec_bv16_t *bv16 = self;
+ if(bv16){
+ /* deinit base */
+ tmedia_codec_audio_deinit(bv16);
+ /* deinit self */
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_bv16_def_s =
+{
+ sizeof(tdav_codec_bv16_t),
+ tdav_codec_bv16_ctor,
+ tdav_codec_bv16_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_bv16_plugin_def_s =
+{
+ &tdav_codec_bv16_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_bv16,
+ "BV16",
+ "BroadVoice16 Rate",
+ TMEDIA_CODEC_FORMAT_BV16,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_bv16_open,
+ tdav_codec_bv16_close,
+ tdav_codec_bv16_encode,
+ tdav_codec_bv16_decode,
+ tdav_codec_bv16_sdp_att_match,
+ tdav_codec_bv16_sdp_att_get,
+ tdav_codec_bv16_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_bv16_plugin_def_t = &tdav_codec_bv16_plugin_def_s;
+
+
+#endif /* HAVE_BV16 */
diff --git a/tinyDAV/src/codecs/bv/tdav_codec_bv32.c b/tinyDAV/src/codecs/bv/tdav_codec_bv32.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyDAV/src/codecs/bv/tdav_codec_bv32.c
diff --git a/tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c b/tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c
new file mode 100644
index 0000000..103ac8d
--- /dev/null
+++ b/tinyDAV/src/codecs/dtmf/tdav_codec_dtmf.c
@@ -0,0 +1,126 @@
+/*
+* 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_dtmf.c
+ * @brief DTMF (RFC 4733) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/dtmf/tdav_codec_dtmf.h"
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+
+/* ============ DTMF Plugin interface ================= */
+
+tsk_size_t tdav_codec_dtmf_fmtp_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ return 0;
+}
+
+tsk_size_t tdav_codec_dtmf_fmtp_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)
+{
+ return 0;
+}
+
+char* tdav_codec_dtmf_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ if(tsk_striequals(att_name, "fmtp")){
+ return tsk_strdup("0-16");
+ }
+ return tsk_null;
+}
+
+tsk_bool_t tdav_codec_dtmf_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// DTMF Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_dtmf_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_dtmf_t *dtmf = self;
+ if(dtmf){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_dtmf_dtor(tsk_object_t * self)
+{
+ tdav_codec_dtmf_t *dtmf = self;
+ if(dtmf){
+ /* deinit base */
+ tmedia_codec_audio_deinit(dtmf);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_dtmf_def_s =
+{
+ sizeof(tdav_codec_dtmf_t),
+ tdav_codec_dtmf_ctor,
+ tdav_codec_dtmf_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_dtmf_plugin_def_s =
+{
+ &tdav_codec_dtmf_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_none, // fake codec without real identifier
+ "telephone-event",
+ "DTMF Codec (RFC 4733)",
+ TMEDIA_CODEC_FORMAT_DTMF,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tsk_null, // open
+ tsk_null, // close
+ tdav_codec_dtmf_fmtp_encode,
+ tdav_codec_dtmf_fmtp_decode,
+ tdav_codec_dtmf_sdp_att_match,
+ tdav_codec_dtmf_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_dtmf_plugin_def_t = &tdav_codec_dtmf_plugin_def_s;
diff --git a/tinyDAV/src/codecs/fec/tdav_codec_red.c b/tinyDAV/src/codecs/fec/tdav_codec_red.c
new file mode 100644
index 0000000..2fb6f27
--- /dev/null
+++ b/tinyDAV/src/codecs/fec/tdav_codec_red.c
@@ -0,0 +1,263 @@
+/*
+* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]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_red.c
+ * @brief RTP Payload for Redundant Audio Data as per RFC 2198
+ */
+#include "tinydav/codecs/fec/tdav_codec_red.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_memory.h"
+#include "tsk_time.h"
+#include "tsk_debug.h"
+
+typedef struct tdav_codec_red_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ tdav_codec_red_rtppacket_cb_f callback;
+ const void* callback_data;
+}
+tdav_codec_red_t;
+
+int tdav_codec_red_set_callback(tdav_codec_red_t *self, tdav_codec_red_rtppacket_cb_f callback, const void* callback_data)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->callback = callback;
+ self->callback_data = callback_data;
+
+ return 0;
+}
+
+static int tdav_codec_red_open(tmedia_codec_t* self)
+{
+ return 0;
+}
+
+static int tdav_codec_red_close(tmedia_codec_t* self)
+{
+ return 0;
+}
+
+static tsk_size_t tdav_codec_red_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_red_t *red = (tdav_codec_red_t *)self;
+ tsk_size_t xsize = (in_size + 1);
+ static const uint8_t __first_octet = 0x00; // F=1, PT=0. Up to the caller to update this first octet with the right PT.
+
+ if(!red || !in_data || !in_size || !out_data || !out_max_size){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(*out_max_size < xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to realloc data");
+ *out_max_size = 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ ((uint8_t*)*out_data)[0] = __first_octet;
+ memcpy(&((uint8_t*)*out_data)[1], in_data, in_size);
+
+ return xsize;
+}
+
+static tsk_size_t tdav_codec_red_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_red_t* red = (tdav_codec_red_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+ trtp_rtp_packet_t* red_rtp_pkt = tsk_null;
+ const uint8_t* pdata = in_data;
+ const uint8_t* red_hdr = in_data;
+ tsk_size_t red_hdrs_count, i;
+ tsk_bool_t last;
+ uint8_t F;
+ uint16_t timestamp_offset, block_length;
+
+ if(!red || !in_data || (in_size < TDAV_CODEC_RED_MIN_PKT_SIZE)|| !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(!red->callback){
+ TSK_DEBUG_WARN("Not callback installed for RED data");
+ return 0;
+ }
+
+ if((F = (pdata[0] & 0x80)) == 0){
+ i = 1;
+ red_hdrs_count = 1;
+ }
+ else{
+ for(i = 0, red_hdrs_count = 0; i < in_size; i+= 4, ++red_hdrs_count){
+ if((F = (pdata[i] & 0x80)) == 0){ ++i; ++red_hdrs_count; break; }
+ }
+ }
+
+ if(i >= in_size){
+ TSK_DEBUG_ERROR("Invalid data");
+ return 0;
+ }
+
+ pdata += i;
+ in_size -= i;
+
+ for(i = 0; i < red_hdrs_count && in_size > 0; ++i){
+ TSK_OBJECT_SAFE_FREE(red_rtp_pkt);
+ if(!(red_rtp_pkt = trtp_rtp_packet_create_null())){
+ TSK_DEBUG_ERROR("Failed to create RTP packet");
+ continue;
+ }
+ if(!(red_rtp_pkt->header = trtp_rtp_header_create(rtp_hdr->ssrc, rtp_hdr->seq_num, rtp_hdr->timestamp, rtp_hdr->payload_type, rtp_hdr->marker))){
+ TSK_DEBUG_ERROR("Failed to create RTP header");
+ continue;
+ }
+
+ // Must create an RTP packet for each RED chunck as they will be saved in the JB
+ last = (i == (red_hdrs_count - 1));
+ F = (red_hdr[0] & 0x80);
+ red_rtp_pkt->header->payload_type = (red_hdr[0] & 0x7F);
+
+ if(last || !F){
+ /*
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |0| Block PT |
+ +-+-+-+-+-+-+-+-+
+ */
+ block_length = (uint16_t)in_size;
+ }
+ else{
+ /*
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |1| block PT=7 | timestamp offset | block length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ timestamp_offset = ((red_hdr[1] << 8) | red_hdr[2]) >> 2;
+ block_length = ((red_hdr[2] & 0x03) << 8) | red_hdr[3];
+ if(block_length > in_size){
+ TSK_DEBUG_ERROR("Invalid 'block length'");
+ break;
+ }
+ red_rtp_pkt->header->timestamp += timestamp_offset;
+ red_hdr += 4;
+ }
+
+ // decode
+ if(red->callback){
+ // do not use "data_const" as payload will be saved in the jitter buffer and decoded later (async)
+ if((red_rtp_pkt->payload.data = tsk_malloc(block_length))){
+ memcpy(red_rtp_pkt->payload.data, pdata, block_length);
+ red_rtp_pkt->payload.size = block_length;
+ red->callback(red->callback_data, red_rtp_pkt);
+ }
+ }
+
+ pdata += block_length;
+ in_size -= block_length;
+ }
+
+ TSK_OBJECT_SAFE_FREE(red_rtp_pkt);
+
+ return 0; // must be always zero
+}
+
+static tsk_bool_t tdav_codec_red_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ return tsk_true;
+}
+
+static char* tdav_codec_red_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ return tsk_null;
+}
+
+
+/* ============ red object definition ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_red_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_red_t *red = self;
+ if(red){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_red_dtor(tsk_object_t * self)
+{
+ tdav_codec_red_t *red = self;
+ if(red){
+ /* deinit base */
+ tmedia_codec_video_deinit(red);
+ /* deinit self */
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_red_def_s =
+{
+ sizeof(tdav_codec_red_t),
+ tdav_codec_red_ctor,
+ tdav_codec_red_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_red_plugin_def_s =
+{
+ &tdav_codec_red_def_s,
+
+ (/* tmedia_video | tmedia_audio | */tmedia_t140), // FIXME: for now is only supported with T.140
+ tmedia_codec_id_red,
+ "red",
+ "red codec",
+ TMEDIA_CODEC_FORMAT_RED,
+ tsk_true,
+ 1000, // rate: FIXME: for now it's only for T.140
+
+ /* audio */
+ { 0 },
+
+ /* video (defaul width,height,fps) */
+ {176, 144, 15},
+
+ tsk_null, // set()
+ tdav_codec_red_open,
+ tdav_codec_red_close,
+ tdav_codec_red_encode,
+ tdav_codec_red_decode,
+ tdav_codec_red_sdp_att_match,
+ tdav_codec_red_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_red_plugin_def_t = &tdav_codec_red_plugin_def_s;
diff --git a/tinyDAV/src/codecs/fec/tdav_codec_ulpfec.c b/tinyDAV/src/codecs/fec/tdav_codec_ulpfec.c
new file mode 100644
index 0000000..f492a52
--- /dev/null
+++ b/tinyDAV/src/codecs/fec/tdav_codec_ulpfec.c
@@ -0,0 +1,424 @@
+/*
+* Copyright (C) 2012-2015 Doubango Telecom <http://www.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_ulpfec.c
+ * @brief Forward Error Correction (FEC) implementation as per RFC 5109
+ */
+#include "tinydav/codecs/fec/tdav_codec_ulpfec.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_FEC_PKT_HDR_SIZE 10
+
+typedef struct tdav_codec_ulpfec_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ struct{
+ struct tdav_fec_pkt_s* pkt;
+ } encoder;
+}
+tdav_codec_ulpfec_t;
+
+//
+// FEC LEVEL
+//
+typedef struct tdav_fec_level_s
+{
+ TSK_DECLARE_OBJECT;
+
+ struct{ // 7.4. FEC Level Header for FEC Packets
+ uint16_t length;
+ uint64_t mask;
+ tsk_size_t mask_size; // in bits
+ } hdr;
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ }payload;
+}tdav_fec_level_t;
+typedef tsk_list_t tdav_fec_levels_L_t;
+static tsk_object_t* tdav_fec_level_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_fec_level_t *level = self;
+ if (level){
+ level->hdr.mask_size = 16; // L=0
+ }
+ return self;
+}
+static tsk_object_t* tdav_fec_level_dtor(tsk_object_t * self)
+{
+ tdav_fec_level_t *level = self;
+ if (level){
+ TSK_FREE(level->payload.ptr);
+ }
+
+ return self;
+}
+static const tsk_object_def_t tdav_fec_level_def_s =
+{
+ sizeof(tdav_fec_level_t),
+ tdav_fec_level_ctor,
+ tdav_fec_level_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tdav_fec_level_def_t = &tdav_fec_level_def_s;
+
+
+//
+// FEC PACKET
+//
+typedef struct tdav_fec_pkt_s
+{
+ TSK_DECLARE_OBJECT;
+
+ struct{ // RFC 5109 - 7.3. FEC Header for FEC Packets
+ unsigned E : 1;
+ unsigned L : 1;
+ unsigned P : 1;
+ unsigned X : 1;
+ unsigned CC : 4;
+ unsigned M : 1;
+ unsigned PT : 7;
+ struct{
+ uint16_t value;
+ unsigned set : 1;
+ }SN_base;
+ uint32_t TS;
+ uint16_t length;
+ }hdr;
+
+ tdav_fec_levels_L_t* levels;
+}
+tdav_fec_pkt_t;
+static tsk_object_t* tdav_fec_pkt_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_fec_pkt_t *pkt = self;
+ if (pkt){
+ if (!(pkt->levels = tsk_list_create())){
+ TSK_DEBUG_ERROR("Failed to create levels");
+ return tsk_null;
+ }
+ }
+ return self;
+}
+static tsk_object_t* tdav_fec_pkt_dtor(tsk_object_t * self)
+{
+ tdav_fec_pkt_t *pkt = self;
+ if (pkt){
+ TSK_OBJECT_SAFE_FREE(pkt->levels);
+ }
+
+ return self;
+}
+static int tdav_fec_pkt_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2)
+{
+ const tdav_fec_pkt_t *p1 = _p1;
+ const tdav_fec_pkt_t *p2 = _p2;
+
+ if (p1 && p2){
+ return (int)(p1->hdr.SN_base.value - p2->hdr.SN_base.value);
+ }
+ else if (!p1 && !p2) return 0;
+ else return -1;
+}
+static const tsk_object_def_t tdav_fec_pkt_def_s =
+{
+ sizeof(tdav_fec_pkt_t),
+ tdav_fec_pkt_ctor,
+ tdav_fec_pkt_dtor,
+ tdav_fec_pkt_cmp,
+};
+const tsk_object_def_t *tdav_fec_pkt_def_t = &tdav_fec_pkt_def_s;
+
+
+tsk_size_t tdav_codec_ulpfec_guess_serialbuff_size(const tdav_codec_ulpfec_t* self)
+{
+ tsk_size_t size = TDAV_FEC_PKT_HDR_SIZE;
+ tsk_list_item_t *item;
+ tdav_fec_level_t* level;
+
+ if (!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ tsk_list_foreach(item, self->encoder.pkt->levels){
+ if (!(level = item->data)){
+ continue;
+ }
+ size += 2 /* Protection length */ + (level->hdr.mask_size >> 3) + level->hdr.length;
+ }
+
+ return size;
+}
+
+int tdav_codec_ulpfec_enc_reset(tdav_codec_ulpfec_t* self)
+{
+ tsk_list_item_t *item;
+ tdav_fec_level_t* level;
+
+ if (!self || !self->encoder.pkt){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return -1;
+ }
+
+ // reset packet
+ memset(&self->encoder.pkt->hdr, 0, sizeof(self->encoder.pkt->hdr));
+
+ // reset levels
+ tsk_list_foreach(item, self->encoder.pkt->levels){
+ if ((level = item->data)){
+ memset(&level->hdr, 0, sizeof(level->hdr));
+ if (level->payload.ptr){
+ memset(level->payload.ptr, 0, level->payload.size);
+ }
+ }
+ }
+ return 0;
+}
+
+int tdav_codec_ulpfec_enc_protect(tdav_codec_ulpfec_t* self, const trtp_rtp_packet_t* rtp_packet)
+{
+ if (!self || !self->encoder.pkt || !rtp_packet || !rtp_packet->header){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return -1;
+ }
+
+ // Packet
+ self->encoder.pkt->hdr.P ^= rtp_packet->header->padding;
+ self->encoder.pkt->hdr.X ^= rtp_packet->header->extension;
+ self->encoder.pkt->hdr.CC ^= rtp_packet->header->csrc_count;
+ self->encoder.pkt->hdr.M ^= rtp_packet->header->marker;
+ self->encoder.pkt->hdr.PT ^= rtp_packet->header->payload_type;
+ if (!self->encoder.pkt->hdr.SN_base.set){
+ self->encoder.pkt->hdr.SN_base.value = rtp_packet->header->seq_num;
+ self->encoder.pkt->hdr.SN_base.set = 1;
+ }
+ else{
+ self->encoder.pkt->hdr.SN_base.value = TSK_MIN(self->encoder.pkt->hdr.SN_base.value, rtp_packet->header->seq_num);
+ }
+ self->encoder.pkt->hdr.TS ^= rtp_packet->header->timestamp;
+ self->encoder.pkt->hdr.length ^= (trtp_rtp_packet_guess_serialbuff_size(rtp_packet) - TRTP_RTP_HEADER_MIN_SIZE);
+
+ // Level
+ // For now, always single-level protection
+ {
+ tdav_fec_level_t* level0 = TSK_LIST_FIRST_DATA(self->encoder.pkt->levels);
+ const uint8_t* rtp_payload = (const uint8_t*)(rtp_packet->payload.data_const ? rtp_packet->payload.data_const : rtp_packet->payload.data);
+ tsk_size_t i;
+ if (!level0){
+ tdav_fec_level_t* _level0;
+ if (!(_level0 = tsk_object_new(tdav_fec_level_def_t))){
+ TSK_DEBUG_ERROR("Failed to create level");
+ return -2;
+ }
+ level0 = _level0;
+ tsk_list_push_back_data(self->encoder.pkt->levels, (void**)&_level0);
+ }
+ if (level0->payload.size < rtp_packet->payload.size){
+ if (!(level0->payload.ptr = tsk_realloc(level0->payload.ptr, rtp_packet->payload.size))){
+ TSK_DEBUG_ERROR("Failed to realloc size %d", rtp_packet->payload.size);
+ level0->payload.size = 0;
+ return -3;
+ }
+ level0->payload.size = rtp_packet->payload.size;
+ }
+ for (i = 0; i < rtp_packet->payload.size; ++i){
+ level0->payload.ptr[i] ^= rtp_payload[i];
+ }
+ level0->hdr.mask_size = self->encoder.pkt->hdr.L ? 48 : 16;
+ level0->hdr.mask |= (uint64_t)((uint64_t)1 << (level0->hdr.mask_size - (rtp_packet->header->seq_num - self->encoder.pkt->hdr.SN_base.value)));
+ level0->hdr.length = (uint16_t)(TSK_MAX(level0->hdr.length, rtp_packet->payload.size));
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_ulpfec_enc_serialize(const tdav_codec_ulpfec_t* self, void** out_data, tsk_size_t* out_max_size)
+{
+ uint8_t* pdata;
+ tsk_size_t xsize;
+ int32_t i;
+ tsk_list_item_t* item;
+ tdav_fec_level_t* level;
+
+ if (!self || !self->encoder.pkt || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+ xsize = tdav_codec_ulpfec_guess_serialbuff_size(self);
+
+ if (*out_max_size < xsize){
+ if (!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to reallocate buffer with size =%d", xsize);
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+ pdata = (uint8_t*)*out_data;
+
+ // E(1), L(1), P(1), X(1), CC(4)
+ pdata[0] =
+ (self->encoder.pkt->hdr.E << 7) |
+ (self->encoder.pkt->hdr.L << 6) |
+ (self->encoder.pkt->hdr.P << 5) |
+ (self->encoder.pkt->hdr.X << 4) |
+ (self->encoder.pkt->hdr.CC & 0x0F);
+ // M(1), PT(7)
+ pdata[1] = (self->encoder.pkt->hdr.M << 7) | (self->encoder.pkt->hdr.PT & 0x7F);
+ // SN base (16)
+ pdata[2] = (self->encoder.pkt->hdr.SN_base.value >> 8);
+ pdata[3] = (self->encoder.pkt->hdr.SN_base.value & 0xFF);
+ // TS (32)
+ pdata[4] = self->encoder.pkt->hdr.TS >> 24;
+ pdata[5] = (self->encoder.pkt->hdr.TS >> 16) & 0xFF;
+ pdata[6] = (self->encoder.pkt->hdr.TS >> 8) & 0xFF;
+ pdata[7] = (self->encoder.pkt->hdr.TS & 0xFF);
+ // Length (16)
+ pdata[8] = (self->encoder.pkt->hdr.length >> 8);
+ pdata[9] = (self->encoder.pkt->hdr.length & 0xFF);
+
+ pdata += 10;
+
+ tsk_list_foreach(item, self->encoder.pkt->levels){
+ if (!(level = item->data)){
+ continue;
+ }
+ // Protection length (16)
+ pdata[0] = (level->hdr.length >> 8);
+ pdata[1] = (level->hdr.length & 0xFF);
+ pdata += 2;
+ // mask (16 or 48)
+ for (i = (int32_t)(level->hdr.mask_size - 8); i >= 0; i -= 8){
+ *pdata = ((level->hdr.mask >> i) & 0xFF); ++pdata;
+ }
+ // payload
+ memcpy(pdata, level->payload.ptr, level->hdr.length);
+ }
+
+ return xsize;
+}
+
+
+
+static int tdav_codec_ulpfec_open(tmedia_codec_t* self)
+{
+ return 0;
+}
+
+static int tdav_codec_ulpfec_close(tmedia_codec_t* self)
+{
+ return 0;
+}
+
+static tsk_size_t tdav_codec_ulpfec_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ TSK_DEBUG_ERROR("Not expected to be called");
+ return 0;
+}
+
+static tsk_size_t tdav_codec_ulpfec_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)
+{
+ TSK_DEBUG_ERROR("Not expected to be called");
+ return 0;
+}
+
+static tsk_bool_t tdav_codec_ulpfec_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ return tsk_true;
+}
+
+static char* tdav_codec_ulpfec_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ return tsk_null;
+}
+
+
+/* ============ ULPFEC object definition ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_ulpfec_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_ulpfec_t *ulpfec = self;
+ if (ulpfec){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ if (!(ulpfec->encoder.pkt = tsk_object_new(tdav_fec_pkt_def_t))){
+ TSK_DEBUG_ERROR("Failed to create FEC packet");
+ return tsk_null;
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_ulpfec_dtor(tsk_object_t * self)
+{
+ tdav_codec_ulpfec_t *ulpfec = self;
+ if (ulpfec){
+ /* deinit base */
+ tmedia_codec_video_deinit(ulpfec);
+ /* deinit self */
+ TSK_OBJECT_SAFE_FREE(ulpfec->encoder.pkt);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_ulpfec_def_s =
+{
+ sizeof(tdav_codec_ulpfec_t),
+ tdav_codec_ulpfec_ctor,
+ tdav_codec_ulpfec_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_ulpfec_plugin_def_s =
+{
+ &tdav_codec_ulpfec_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_none, // fake codec
+ "ulpfec",
+ "ulpfec codec",
+ TMEDIA_CODEC_FORMAT_ULPFEC,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (defaul width,height,fps) */
+ { 176, 144, 15 },
+
+ tsk_null, // set()
+ tdav_codec_ulpfec_open,
+ tdav_codec_ulpfec_close,
+ tdav_codec_ulpfec_encode,
+ tdav_codec_ulpfec_decode,
+ tdav_codec_ulpfec_sdp_att_match,
+ tdav_codec_ulpfec_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_ulpfec_plugin_def_t = &tdav_codec_ulpfec_plugin_def_s; \ No newline at end of file
diff --git a/tinyDAV/src/codecs/g711/g711.c b/tinyDAV/src/codecs/g711/g711.c
new file mode 100644
index 0000000..fa7c8be
--- /dev/null
+++ b/tinyDAV/src/codecs/g711/g711.c
@@ -0,0 +1,295 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+
+#include "tinydav/codecs/g711/g711.h"
+
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static short search(short val, short *table, short size)
+{
+ short i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char linear2alaw(short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char aval;
+
+ pcm_val = pcm_val >> 3;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+short alaw2linear(unsigned char a_val)
+{
+ short t;
+ short seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char linear2ulaw(short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ pcm_val = pcm_val >> 2;
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+short ulaw2linear(unsigned char u_val)
+{
+ short t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char alaw2ulaw(unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char ulaw2alaw(unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
diff --git a/tinyDAV/src/codecs/g711/tdav_codec_g711.c b/tinyDAV/src/codecs/g711/tdav_codec_g711.c
new file mode 100644
index 0000000..fa970e1
--- /dev/null
+++ b/tinyDAV/src/codecs/g711/tdav_codec_g711.c
@@ -0,0 +1,326 @@
+/*
+* 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_g711.c
+ * @brief G.711u and G.711a (a.k.a PCMU and PCMA) codec plugins.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/g711/tdav_codec_g711.h"
+
+#include "tinydav/codecs/g711/g711.h" /* algorithms */
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ G.711u Plugin interface ================= */
+
+#define tdav_codec_g711u_open tsk_null
+#define tdav_codec_g711u_close tsk_null
+#define tdav_codec_g711u_sdp_att_get tsk_null
+
+static tsk_size_t tdav_codec_g711u_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ register tsk_size_t i;
+ register uint8_t* pout_data;
+ register int16_t* pin_data;
+ tsk_size_t out_size;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = (in_size >> 1);
+
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ pout_data = *out_data;
+ pin_data = (int16_t*)in_data;
+ for(i = 0; i<out_size; i++){
+ pout_data[i] = linear2ulaw(pin_data[i]);
+ }
+
+ return out_size;
+}
+
+static tsk_size_t tdav_codec_g711u_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)
+{
+ tsk_size_t i;
+ tsk_size_t out_size;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = (in_size << 1);
+
+ /* allocate new 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ for(i = 0; i<in_size; i++){
+ ((short*)*out_data)[i] = ulaw2linear(((uint8_t*)in_data)[i]);
+ }
+
+ return out_size;
+}
+
+static tsk_bool_t tdav_codec_g711u_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// G.711u Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g711u_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g711u_t *g711u = self;
+ if(g711u){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g711u_dtor(tsk_object_t * self)
+{
+ tdav_codec_g711u_t *g711u = self;
+ if(g711u){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g711u);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g711u_def_s =
+{
+ sizeof(tdav_codec_g711u_t),
+ tdav_codec_g711u_ctor,
+ tdav_codec_g711u_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g711u_plugin_def_s =
+{
+ &tdav_codec_g711u_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_pcmu,
+ "PCMU",
+ "G.711u codec (native)",
+ TMEDIA_CODEC_FORMAT_G711u,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_g711u_open,
+ tdav_codec_g711u_close,
+ tdav_codec_g711u_encode,
+ tdav_codec_g711u_decode,
+ tdav_codec_g711u_sdp_att_match,
+ tdav_codec_g711u_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g711u_plugin_def_t = &tdav_codec_g711u_plugin_def_s;
+
+
+/* ============ G.711a Plugin interface ================= */
+
+#define tdav_codec_g711a_open tsk_null
+#define tdav_codec_g711a_close tsk_null
+#define tdav_codec_g711a_sdp_att_get tsk_null
+
+static tsk_size_t tdav_codec_g711a_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ register tsk_size_t i;
+ register uint8_t* pout_data;
+ register int16_t* pin_data;
+ tsk_size_t out_size;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = (in_size >> 1);
+
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ pout_data = *out_data;
+ pin_data = (int16_t*)in_data;
+ for(i = 0; i<out_size; i++){
+ pout_data[i] = linear2alaw(pin_data[i]);
+ }
+
+ return out_size;
+}
+
+#if 0
+FILE* file = tsk_null;
+int count = 0;
+#endif
+static tsk_size_t tdav_codec_g711a_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)
+{
+ tsk_size_t i, out_size;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+ out_size = (in_size << 1);
+#if 0
+ if(!file && count<=1000){
+ file = fopen("./g711a.pcm", "wb");
+ }
+#endif
+ /* allocate new 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ for(i = 0; i<in_size; i++){
+ ((short*)*out_data)[i] = alaw2linear(((uint8_t*)in_data)[i]);
+ }
+#if 0
+ if(++count<=1000){
+ fwrite(*out_data, sizeof(short), in_size, file);
+ }
+ else if(file){
+ fclose(file);
+ file = tsk_null;
+ }
+#endif
+ return out_size;
+}
+
+static tsk_bool_t tdav_codec_g711a_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// G.711a Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g711a_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g711a_t *g711a = self;
+ if(g711a){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g711a_dtor(tsk_object_t * self)
+{
+ tdav_codec_g711a_t *g711a = self;
+ if(g711a){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g711a);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g711a_def_s =
+{
+ sizeof(tdav_codec_g711a_t),
+ tdav_codec_g711a_ctor,
+ tdav_codec_g711a_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g711a_plugin_def_s =
+{
+ &tdav_codec_g711a_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_pcma,
+ "PCMA",
+ "G.711a codec (native)",
+ TMEDIA_CODEC_FORMAT_G711a,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_g711a_open,
+ tdav_codec_g711a_close,
+ tdav_codec_g711a_encode,
+ tdav_codec_g711a_decode,
+ tdav_codec_g711a_sdp_att_match,
+ tdav_codec_g711a_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g711a_plugin_def_t = &tdav_codec_g711a_plugin_def_s;
diff --git a/tinyDAV/src/codecs/g722/g722_decode.c b/tinyDAV/src/codecs/g722/g722_decode.c
new file mode 100644
index 0000000..b6b7830
--- /dev/null
+++ b/tinyDAV/src/codecs/g722/g722_decode.c
@@ -0,0 +1,400 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * g722_decode.c - The ITU G.722 codec, decode part.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * Despite my general liking of the GPL, I place my own contributions
+ * to this code in the public domain for the benefit of all mankind -
+ * even the slimy ones who might try to proprietize my work and use it
+ * to my detriment.
+ *
+ * Based in part on a single channel G.722 codec which is:
+ *
+ * Copyright (c) CMU 1993
+ * Computer Science, Speech Group
+ * Chengxiang Lu and Alex Hauptmann
+ *
+ * $Id: g722_decode.c,v 1.15 2006/07/07 16:37:49 steveu Exp $
+ *
+ * Modifications for WebRtc, 2011/04/28, by tlegrand:
+ * -Removed usage of inttypes.h and tgmath.h
+ * -Changed to use WebRtc types
+ * -Changed __inline__ to __inline
+ * -Added saturation check on output
+ */
+
+/*! \file */
+
+#include <stdio.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include "tinydav/codecs/g722/g722_enc_dec.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+static __inline int16_t saturate(int32_t amp)
+{
+ int16_t amp16;
+
+ /* Hopefully this is optimised for the common case - not clipping */
+ amp16 = (int16_t) amp;
+ if (amp == amp16)
+ return amp16;
+ if (amp > TDAV_INT16_MAX)
+ return TDAV_INT16_MAX;
+ return TDAV_INT16_MIN;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void block4(g722_decode_state_t *s, int band, int d);
+
+static void block4(g722_decode_state_t *s, int band, int d)
+{
+ int wd1;
+ int wd2;
+ int wd3;
+ int i;
+
+ /* Block 4, RECONS */
+ s->band[band].d[0] = d;
+ s->band[band].r[0] = saturate(s->band[band].s + d);
+
+ /* Block 4, PARREC */
+ s->band[band].p[0] = saturate(s->band[band].sz + d);
+
+ /* Block 4, UPPOL2 */
+ for (i = 0; i < 3; i++)
+ s->band[band].sg[i] = s->band[band].p[i] >> 15;
+ wd1 = saturate(s->band[band].a[1] << 2);
+
+ wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1;
+ if (wd2 > 32767)
+ wd2 = 32767;
+ wd3 = (s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128;
+ wd3 += (wd2 >> 7);
+ wd3 += (s->band[band].a[2]*32512) >> 15;
+ if (wd3 > 12288)
+ wd3 = 12288;
+ else if (wd3 < -12288)
+ wd3 = -12288;
+ s->band[band].ap[2] = wd3;
+
+ /* Block 4, UPPOL1 */
+ s->band[band].sg[0] = s->band[band].p[0] >> 15;
+ s->band[band].sg[1] = s->band[band].p[1] >> 15;
+ wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192;
+ wd2 = (s->band[band].a[1]*32640) >> 15;
+
+ s->band[band].ap[1] = saturate(wd1 + wd2);
+ wd3 = saturate(15360 - s->band[band].ap[2]);
+ if (s->band[band].ap[1] > wd3)
+ s->band[band].ap[1] = wd3;
+ else if (s->band[band].ap[1] < -wd3)
+ s->band[band].ap[1] = -wd3;
+
+ /* Block 4, UPZERO */
+ wd1 = (d == 0) ? 0 : 128;
+ s->band[band].sg[0] = d >> 15;
+ for (i = 1; i < 7; i++)
+ {
+ s->band[band].sg[i] = s->band[band].d[i] >> 15;
+ wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1;
+ wd3 = (s->band[band].b[i]*32640) >> 15;
+ s->band[band].bp[i] = saturate(wd2 + wd3);
+ }
+
+ /* Block 4, DELAYA */
+ for (i = 6; i > 0; i--)
+ {
+ s->band[band].d[i] = s->band[band].d[i - 1];
+ s->band[band].b[i] = s->band[band].bp[i];
+ }
+
+ for (i = 2; i > 0; i--)
+ {
+ s->band[band].r[i] = s->band[band].r[i - 1];
+ s->band[band].p[i] = s->band[band].p[i - 1];
+ s->band[band].a[i] = s->band[band].ap[i];
+ }
+
+ /* Block 4, FILTEP */
+ wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]);
+ wd1 = (s->band[band].a[1]*wd1) >> 15;
+ wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]);
+ wd2 = (s->band[band].a[2]*wd2) >> 15;
+ s->band[band].sp = saturate(wd1 + wd2);
+
+ /* Block 4, FILTEZ */
+ s->band[band].sz = 0;
+ for (i = 6; i > 0; i--)
+ {
+ wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]);
+ s->band[band].sz += (s->band[band].b[i]*wd1) >> 15;
+ }
+ s->band[band].sz = saturate(s->band[band].sz);
+
+ /* Block 4, PREDIC */
+ s->band[band].s = saturate(s->band[band].sp + s->band[band].sz);
+}
+/*- End of function --------------------------------------------------------*/
+
+g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options)
+{
+ if (s == NULL)
+ {
+ if ((s = (g722_decode_state_t *) malloc(sizeof(*s))) == NULL)
+ return NULL;
+ }
+ memset(s, 0, sizeof(*s));
+ if (rate == 48000)
+ s->bits_per_sample = 6;
+ else if (rate == 56000)
+ s->bits_per_sample = 7;
+ else
+ s->bits_per_sample = 8;
+ if ((options & G722_SAMPLE_RATE_8000))
+ s->eight_k = TRUE;
+ if ((options & G722_PACKED) && s->bits_per_sample != 8)
+ s->packed = TRUE;
+ else
+ s->packed = FALSE;
+ s->band[0].det = 32;
+ s->band[1].det = 8;
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_decode_release(g722_decode_state_t *s)
+{
+ free(s);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_decode(g722_decode_state_t *s, int16_t amp[],
+ const uint8_t g722_data[], int len)
+{
+ static const int wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042 };
+ static const int rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 };
+ static const int ilb[32] =
+ {
+ 2048, 2093, 2139, 2186, 2233, 2282, 2332,
+ 2383, 2435, 2489, 2543, 2599, 2656, 2714,
+ 2774, 2834, 2896, 2960, 3025, 3091, 3158,
+ 3228, 3298, 3371, 3444, 3520, 3597, 3676,
+ 3756, 3838, 3922, 4008
+ };
+ static const int wh[3] = {0, -214, 798};
+ static const int rh2[4] = {2, 1, 2, 1};
+ static const int qm2[4] = {-7408, -1616, 7408, 1616};
+ static const int qm4[16] =
+ {
+ 0, -20456, -12896, -8968,
+ -6288, -4240, -2584, -1200,
+ 20456, 12896, 8968, 6288,
+ 4240, 2584, 1200, 0
+ };
+ static const int qm5[32] =
+ {
+ -280, -280, -23352, -17560,
+ -14120, -11664, -9752, -8184,
+ -6864, -5712, -4696, -3784,
+ -2960, -2208, -1520, -880,
+ 23352, 17560, 14120, 11664,
+ 9752, 8184, 6864, 5712,
+ 4696, 3784, 2960, 2208,
+ 1520, 880, 280, -280
+ };
+ static const int qm6[64] =
+ {
+ -136, -136, -136, -136,
+ -24808, -21904, -19008, -16704,
+ -14984, -13512, -12280, -11192,
+ -10232, -9360, -8576, -7856,
+ -7192, -6576, -6000, -5456,
+ -4944, -4464, -4008, -3576,
+ -3168, -2776, -2400, -2032,
+ -1688, -1360, -1040, -728,
+ 24808, 21904, 19008, 16704,
+ 14984, 13512, 12280, 11192,
+ 10232, 9360, 8576, 7856,
+ 7192, 6576, 6000, 5456,
+ 4944, 4464, 4008, 3576,
+ 3168, 2776, 2400, 2032,
+ 1688, 1360, 1040, 728,
+ 432, 136, -432, -136
+ };
+ static const int qmf_coeffs[12] =
+ {
+ 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11,
+ };
+
+ int dlowt;
+ int rlow;
+ int ihigh;
+ int dhigh;
+ int rhigh;
+ int xout1;
+ int xout2;
+ int wd1;
+ int wd2;
+ int wd3;
+ int code;
+ int outlen;
+ int i;
+ int j;
+
+ outlen = 0;
+ rhigh = 0;
+ for (j = 0; j < len; )
+ {
+ if (s->packed)
+ {
+ /* Unpack the code bits */
+ if (s->in_bits < s->bits_per_sample)
+ {
+ s->in_buffer |= (g722_data[j++] << s->in_bits);
+ s->in_bits += 8;
+ }
+ code = s->in_buffer & ((1 << s->bits_per_sample) - 1);
+ s->in_buffer >>= s->bits_per_sample;
+ s->in_bits -= s->bits_per_sample;
+ }
+ else
+ {
+ code = g722_data[j++];
+ }
+
+ switch (s->bits_per_sample)
+ {
+ default:
+ case 8:
+ wd1 = code & 0x3F;
+ ihigh = (code >> 6) & 0x03;
+ wd2 = qm6[wd1];
+ wd1 >>= 2;
+ break;
+ case 7:
+ wd1 = code & 0x1F;
+ ihigh = (code >> 5) & 0x03;
+ wd2 = qm5[wd1];
+ wd1 >>= 1;
+ break;
+ case 6:
+ wd1 = code & 0x0F;
+ ihigh = (code >> 4) & 0x03;
+ wd2 = qm4[wd1];
+ break;
+ }
+ /* Block 5L, LOW BAND INVQBL */
+ wd2 = (s->band[0].det*wd2) >> 15;
+ /* Block 5L, RECONS */
+ rlow = s->band[0].s + wd2;
+ /* Block 6L, LIMIT */
+ if (rlow > 16383)
+ rlow = 16383;
+ else if (rlow < -16384)
+ rlow = -16384;
+
+ /* Block 2L, INVQAL */
+ wd2 = qm4[wd1];
+ dlowt = (s->band[0].det*wd2) >> 15;
+
+ /* Block 3L, LOGSCL */
+ wd2 = rl42[wd1];
+ wd1 = (s->band[0].nb*127) >> 7;
+ wd1 += wl[wd2];
+ if (wd1 < 0)
+ wd1 = 0;
+ else if (wd1 > 18432)
+ wd1 = 18432;
+ s->band[0].nb = wd1;
+
+ /* Block 3L, SCALEL */
+ wd1 = (s->band[0].nb >> 6) & 31;
+ wd2 = 8 - (s->band[0].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[0].det = wd3 << 2;
+
+ block4(s, 0, dlowt);
+
+ if (!s->eight_k)
+ {
+ /* Block 2H, INVQAH */
+ wd2 = qm2[ihigh];
+ dhigh = (s->band[1].det*wd2) >> 15;
+ /* Block 5H, RECONS */
+ rhigh = dhigh + s->band[1].s;
+ /* Block 6H, LIMIT */
+ if (rhigh > 16383)
+ rhigh = 16383;
+ else if (rhigh < -16384)
+ rhigh = -16384;
+
+ /* Block 2H, INVQAH */
+ wd2 = rh2[ihigh];
+ wd1 = (s->band[1].nb*127) >> 7;
+ wd1 += wh[wd2];
+ if (wd1 < 0)
+ wd1 = 0;
+ else if (wd1 > 22528)
+ wd1 = 22528;
+ s->band[1].nb = wd1;
+
+ /* Block 3H, SCALEH */
+ wd1 = (s->band[1].nb >> 6) & 31;
+ wd2 = 10 - (s->band[1].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[1].det = wd3 << 2;
+
+ block4(s, 1, dhigh);
+ }
+
+ if (s->itu_test_mode)
+ {
+ amp[outlen++] = (int16_t) (rlow << 1);
+ amp[outlen++] = (int16_t) (rhigh << 1);
+ }
+ else
+ {
+ if (s->eight_k)
+ {
+ amp[outlen++] = (int16_t) (rlow << 1);
+ }
+ else
+ {
+ /* Apply the receive QMF */
+ for (i = 0; i < 22; i++)
+ s->x[i] = s->x[i + 2];
+ s->x[22] = rlow + rhigh;
+ s->x[23] = rlow - rhigh;
+
+ xout1 = 0;
+ xout2 = 0;
+ for (i = 0; i < 12; i++)
+ {
+ xout2 += s->x[2*i]*qmf_coeffs[i];
+ xout1 += s->x[2*i + 1]*qmf_coeffs[11 - i];
+ }
+ /* We shift by 12 to allow for the QMF filters (DC gain = 4096), less 1
+ to allow for the 15 bit input to the G.722 algorithm. */
+ /* WebRtc, tlegrand: added saturation */
+ amp[outlen++] = saturate(xout1 >> 11);
+ amp[outlen++] = saturate(xout2 >> 11);
+ }
+ }
+ }
+ return outlen;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/tinyDAV/src/codecs/g722/g722_encode.c b/tinyDAV/src/codecs/g722/g722_encode.c
new file mode 100644
index 0000000..68758eb
--- /dev/null
+++ b/tinyDAV/src/codecs/g722/g722_encode.c
@@ -0,0 +1,426 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * g722_encode.c - The ITU G.722 codec, encode part.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * Despite my general liking of the GPL, I place my own contributions
+ * to this code in the public domain for the benefit of all mankind -
+ * even the slimy ones who might try to proprietize my work and use it
+ * to my detriment.
+ *
+ * Based on a single channel 64kbps only G.722 codec which is:
+ *
+ ***** Copyright (c) CMU 1993 *****
+ * Computer Science, Speech Group
+ * Chengxiang Lu and Alex Hauptmann
+ *
+ * $Id: g722_encode.c,v 1.14 2006/07/07 16:37:49 steveu Exp $
+ *
+ * Modifications for WebRtc, 2011/04/28, by tlegrand:
+ * -Removed usage of inttypes.h and tgmath.h
+ * -Changed to use WebRtc types
+ * -Added option to run encoder bitexact with ITU-T reference implementation
+ */
+
+#include <stdio.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include "tinydav/codecs/g722/g722_enc_dec.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+static __inline int16_t saturate(int32_t amp)
+{
+ int16_t amp16;
+
+ /* Hopefully this is optimised for the common case - not clipping */
+ amp16 = (int16_t) amp;
+ if (amp == amp16)
+ return amp16;
+ if (amp > TDAV_INT16_MAX)
+ return TDAV_INT16_MAX;
+ return TDAV_INT16_MIN;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void block4(g722_encode_state_t *s, int band, int d)
+{
+ int wd1;
+ int wd2;
+ int wd3;
+ int i;
+
+ /* Block 4, RECONS */
+ s->band[band].d[0] = d;
+ s->band[band].r[0] = saturate(s->band[band].s + d);
+
+ /* Block 4, PARREC */
+ s->band[band].p[0] = saturate(s->band[band].sz + d);
+
+ /* Block 4, UPPOL2 */
+ for (i = 0; i < 3; i++)
+ s->band[band].sg[i] = s->band[band].p[i] >> 15;
+ wd1 = saturate(s->band[band].a[1] << 2);
+
+ wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1;
+ if (wd2 > 32767)
+ wd2 = 32767;
+ wd3 = (wd2 >> 7) + ((s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128);
+ wd3 += (s->band[band].a[2]*32512) >> 15;
+ if (wd3 > 12288)
+ wd3 = 12288;
+ else if (wd3 < -12288)
+ wd3 = -12288;
+ s->band[band].ap[2] = wd3;
+
+ /* Block 4, UPPOL1 */
+ s->band[band].sg[0] = s->band[band].p[0] >> 15;
+ s->band[band].sg[1] = s->band[band].p[1] >> 15;
+ wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192;
+ wd2 = (s->band[band].a[1]*32640) >> 15;
+
+ s->band[band].ap[1] = saturate(wd1 + wd2);
+ wd3 = saturate(15360 - s->band[band].ap[2]);
+ if (s->band[band].ap[1] > wd3)
+ s->band[band].ap[1] = wd3;
+ else if (s->band[band].ap[1] < -wd3)
+ s->band[band].ap[1] = -wd3;
+
+ /* Block 4, UPZERO */
+ wd1 = (d == 0) ? 0 : 128;
+ s->band[band].sg[0] = d >> 15;
+ for (i = 1; i < 7; i++)
+ {
+ s->band[band].sg[i] = s->band[band].d[i] >> 15;
+ wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1;
+ wd3 = (s->band[band].b[i]*32640) >> 15;
+ s->band[band].bp[i] = saturate(wd2 + wd3);
+ }
+
+ /* Block 4, DELAYA */
+ for (i = 6; i > 0; i--)
+ {
+ s->band[band].d[i] = s->band[band].d[i - 1];
+ s->band[band].b[i] = s->band[band].bp[i];
+ }
+
+ for (i = 2; i > 0; i--)
+ {
+ s->band[band].r[i] = s->band[band].r[i - 1];
+ s->band[band].p[i] = s->band[band].p[i - 1];
+ s->band[band].a[i] = s->band[band].ap[i];
+ }
+
+ /* Block 4, FILTEP */
+ wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]);
+ wd1 = (s->band[band].a[1]*wd1) >> 15;
+ wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]);
+ wd2 = (s->band[band].a[2]*wd2) >> 15;
+ s->band[band].sp = saturate(wd1 + wd2);
+
+ /* Block 4, FILTEZ */
+ s->band[band].sz = 0;
+ for (i = 6; i > 0; i--)
+ {
+ wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]);
+ s->band[band].sz += (s->band[band].b[i]*wd1) >> 15;
+ }
+ s->band[band].sz = saturate(s->band[band].sz);
+
+ /* Block 4, PREDIC */
+ s->band[band].s = saturate(s->band[band].sp + s->band[band].sz);
+}
+/*- End of function --------------------------------------------------------*/
+
+g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options)
+{
+ if (s == NULL)
+ {
+ if ((s = (g722_encode_state_t *) malloc(sizeof(*s))) == NULL)
+ return NULL;
+ }
+ memset(s, 0, sizeof(*s));
+ if (rate == 48000)
+ s->bits_per_sample = 6;
+ else if (rate == 56000)
+ s->bits_per_sample = 7;
+ else
+ s->bits_per_sample = 8;
+ if ((options & G722_SAMPLE_RATE_8000))
+ s->eight_k = TRUE;
+ if ((options & G722_PACKED) && s->bits_per_sample != 8)
+ s->packed = TRUE;
+ else
+ s->packed = FALSE;
+ s->band[0].det = 32;
+ s->band[1].det = 8;
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+int g722_encode_release(g722_encode_state_t *s)
+{
+ free(s);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+/* WebRtc, tlegrand:
+ * Only define the following if bit-exactness with reference implementation
+ * is needed. Will only have any effect if input signal is saturated.
+ */
+//#define RUN_LIKE_REFERENCE_G722
+#ifdef RUN_LIKE_REFERENCE_G722
+int16_t limitValues (int16_t rl)
+{
+
+ int16_t yl;
+
+ yl = (rl > 16383) ? 16383 : ((rl < -16384) ? -16384 : rl);
+
+ return (yl);
+}
+#endif
+
+int g722_encode(g722_encode_state_t *s, uint8_t g722_data[],
+ const int16_t amp[], int len)
+{
+ static const int q6[32] =
+ {
+ 0, 35, 72, 110, 150, 190, 233, 276,
+ 323, 370, 422, 473, 530, 587, 650, 714,
+ 786, 858, 940, 1023, 1121, 1219, 1339, 1458,
+ 1612, 1765, 1980, 2195, 2557, 2919, 0, 0
+ };
+ static const int iln[32] =
+ {
+ 0, 63, 62, 31, 30, 29, 28, 27,
+ 26, 25, 24, 23, 22, 21, 20, 19,
+ 18, 17, 16, 15, 14, 13, 12, 11,
+ 10, 9, 8, 7, 6, 5, 4, 0
+ };
+ static const int ilp[32] =
+ {
+ 0, 61, 60, 59, 58, 57, 56, 55,
+ 54, 53, 52, 51, 50, 49, 48, 47,
+ 46, 45, 44, 43, 42, 41, 40, 39,
+ 38, 37, 36, 35, 34, 33, 32, 0
+ };
+ static const int wl[8] =
+ {
+ -60, -30, 58, 172, 334, 538, 1198, 3042
+ };
+ static const int rl42[16] =
+ {
+ 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0
+ };
+ static const int ilb[32] =
+ {
+ 2048, 2093, 2139, 2186, 2233, 2282, 2332,
+ 2383, 2435, 2489, 2543, 2599, 2656, 2714,
+ 2774, 2834, 2896, 2960, 3025, 3091, 3158,
+ 3228, 3298, 3371, 3444, 3520, 3597, 3676,
+ 3756, 3838, 3922, 4008
+ };
+ static const int qm4[16] =
+ {
+ 0, -20456, -12896, -8968,
+ -6288, -4240, -2584, -1200,
+ 20456, 12896, 8968, 6288,
+ 4240, 2584, 1200, 0
+ };
+ static const int qm2[4] =
+ {
+ -7408, -1616, 7408, 1616
+ };
+ static const int qmf_coeffs[12] =
+ {
+ 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11,
+ };
+ static const int ihn[3] = {0, 1, 0};
+ static const int ihp[3] = {0, 3, 2};
+ static const int wh[3] = {0, -214, 798};
+ static const int rh2[4] = {2, 1, 2, 1};
+
+ int dlow;
+ int dhigh;
+ int el;
+ int wd;
+ int wd1;
+ int ril;
+ int wd2;
+ int il4;
+ int ih2;
+ int wd3;
+ int eh;
+ int mih;
+ int i;
+ int j;
+ /* Low and high band PCM from the QMF */
+ int xlow;
+ int xhigh;
+ int g722_bytes;
+ /* Even and odd tap accumulators */
+ int sumeven;
+ int sumodd;
+ int ihigh;
+ int ilow;
+ int code;
+
+ g722_bytes = 0;
+ xhigh = 0;
+ for (j = 0; j < len; )
+ {
+ if (s->itu_test_mode)
+ {
+ xlow =
+ xhigh = amp[j++] >> 1;
+ }
+ else
+ {
+ if (s->eight_k)
+ {
+ /* We shift by 1 to allow for the 15 bit input to the G.722 algorithm. */
+ xlow = amp[j++] >> 1;
+ }
+ else
+ {
+ /* Apply the transmit QMF */
+ /* Shuffle the buffer down */
+ for (i = 0; i < 22; i++)
+ s->x[i] = s->x[i + 2];
+ s->x[22] = amp[j++];
+ s->x[23] = amp[j++];
+
+ /* Discard every other QMF output */
+ sumeven = 0;
+ sumodd = 0;
+ for (i = 0; i < 12; i++)
+ {
+ sumodd += s->x[2*i]*qmf_coeffs[i];
+ sumeven += s->x[2*i + 1]*qmf_coeffs[11 - i];
+ }
+ /* We shift by 12 to allow for the QMF filters (DC gain = 4096), plus 1
+ to allow for us summing two filters, plus 1 to allow for the 15 bit
+ input to the G.722 algorithm. */
+ xlow = (sumeven + sumodd) >> 14;
+ xhigh = (sumeven - sumodd) >> 14;
+
+#ifdef RUN_LIKE_REFERENCE_G722
+ /* The following lines are only used to verify bit-exactness
+ * with reference implementation of G.722. Higher precision
+ * is achieved without limiting the values.
+ */
+ xlow = limitValues(xlow);
+ xhigh = limitValues(xhigh);
+#endif
+ }
+ }
+ /* Block 1L, SUBTRA */
+ el = saturate(xlow - s->band[0].s);
+
+ /* Block 1L, QUANTL */
+ wd = (el >= 0) ? el : -(el + 1);
+
+ for (i = 1; i < 30; i++)
+ {
+ wd1 = (q6[i]*s->band[0].det) >> 12;
+ if (wd < wd1)
+ break;
+ }
+ ilow = (el < 0) ? iln[i] : ilp[i];
+
+ /* Block 2L, INVQAL */
+ ril = ilow >> 2;
+ wd2 = qm4[ril];
+ dlow = (s->band[0].det*wd2) >> 15;
+
+ /* Block 3L, LOGSCL */
+ il4 = rl42[ril];
+ wd = (s->band[0].nb*127) >> 7;
+ s->band[0].nb = wd + wl[il4];
+ if (s->band[0].nb < 0)
+ s->band[0].nb = 0;
+ else if (s->band[0].nb > 18432)
+ s->band[0].nb = 18432;
+
+ /* Block 3L, SCALEL */
+ wd1 = (s->band[0].nb >> 6) & 31;
+ wd2 = 8 - (s->band[0].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[0].det = wd3 << 2;
+
+ block4(s, 0, dlow);
+
+ if (s->eight_k)
+ {
+ /* Just leave the high bits as zero */
+ code = (0xC0 | ilow) >> (8 - s->bits_per_sample);
+ }
+ else
+ {
+ /* Block 1H, SUBTRA */
+ eh = saturate(xhigh - s->band[1].s);
+
+ /* Block 1H, QUANTH */
+ wd = (eh >= 0) ? eh : -(eh + 1);
+ wd1 = (564*s->band[1].det) >> 12;
+ mih = (wd >= wd1) ? 2 : 1;
+ ihigh = (eh < 0) ? ihn[mih] : ihp[mih];
+
+ /* Block 2H, INVQAH */
+ wd2 = qm2[ihigh];
+ dhigh = (s->band[1].det*wd2) >> 15;
+
+ /* Block 3H, LOGSCH */
+ ih2 = rh2[ihigh];
+ wd = (s->band[1].nb*127) >> 7;
+ s->band[1].nb = wd + wh[ih2];
+ if (s->band[1].nb < 0)
+ s->band[1].nb = 0;
+ else if (s->band[1].nb > 22528)
+ s->band[1].nb = 22528;
+
+ /* Block 3H, SCALEH */
+ wd1 = (s->band[1].nb >> 6) & 31;
+ wd2 = 10 - (s->band[1].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ s->band[1].det = wd3 << 2;
+
+ block4(s, 1, dhigh);
+ code = ((ihigh << 6) | ilow) >> (8 - s->bits_per_sample);
+ }
+
+ if (s->packed)
+ {
+ /* Pack the code bits */
+ s->out_buffer |= (code << s->out_bits);
+ s->out_bits += s->bits_per_sample;
+ if (s->out_bits >= 8)
+ {
+ g722_data[g722_bytes++] = (uint8_t) (s->out_buffer & 0xFF);
+ s->out_bits -= 8;
+ s->out_buffer >>= 8;
+ }
+ }
+ else
+ {
+ g722_data[g722_bytes++] = (uint8_t) code;
+ }
+ }
+ return g722_bytes;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/tinyDAV/src/codecs/g722/tdav_codec_g722.c b/tinyDAV/src/codecs/g722/tdav_codec_g722.c
new file mode 100644
index 0000000..749fa04
--- /dev/null
+++ b/tinyDAV/src/codecs/g722/tdav_codec_g722.c
@@ -0,0 +1,219 @@
+/*
+* Copyright (C) 2011-2015 Doubango Telecom <http://www.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_g722.c
+ * @brief G.722 codec plugins.
+ */
+#include "tinydav/codecs/g722/tdav_codec_g722.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+typedef struct tdav_codec_g722_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ g722_encode_state_t *enc_state;
+ g722_decode_state_t *dec_state;
+}
+tdav_codec_g722_t;
+
+static int tdav_codec_g722_open(tmedia_codec_t* self)
+{
+ tdav_codec_g722_t* g722 = (tdav_codec_g722_t*)self;
+
+ if (!g722){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ // Initialize the decoder
+ if (!g722->dec_state){
+ if (!(g722->dec_state = tsk_calloc(1, sizeof(g722_decode_state_t)))){
+ TSK_DEBUG_ERROR("Failed to create G.722 decoder state");
+ return -2;
+ }
+ // Create and/or reset the G.722 decoder
+ // Bitrate 64 kbps and wideband mode (2)
+ if (!(g722->dec_state = g722_decode_init(g722->dec_state, 64000, 2))){
+ TSK_DEBUG_ERROR("g722_decode_init failed");
+ return -3;
+ }
+ }
+
+ // Initialize the encoder
+ if (!g722->enc_state){
+ if (!(g722->enc_state = tsk_calloc(1, sizeof(g722_encode_state_t)))){
+ TSK_DEBUG_ERROR("Failed to create G.722 encoder state");
+ return -4;
+ }
+ // Create and/or reset the G.722 encoder
+ // Bitrate 64 kbps and wideband mode (2)
+ if (!(g722->enc_state = g722_encode_init(g722->enc_state, 64000, 2))){
+ TSK_DEBUG_ERROR("g722_encode_init failed");
+ return -5;
+ }
+ }
+
+ return 0;
+}
+
+static int tdav_codec_g722_close(tmedia_codec_t* self)
+{
+ tdav_codec_g722_t* g722 = (tdav_codec_g722_t*)self;
+
+ (void)(g722);
+
+ /* resources will be freed by the dctor() */
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_g722_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_g722_size;
+ tdav_codec_g722_t* g722 = (tdav_codec_g722_t*)self;
+
+ if (!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_g722_size = in_size >> 2;
+
+ if (*out_max_size < out_g722_size){
+ if (!(*out_data = tsk_realloc(*out_data, out_g722_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = out_g722_size;
+ }
+
+ g722_encode(g722->enc_state, (uint8_t*)*out_data, (int16_t*)in_data, (int)in_size / sizeof(int16_t));
+
+ return out_g722_size;
+}
+
+static tsk_size_t tdav_codec_g722_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_g722_t* g722 = (tdav_codec_g722_t*)self;
+
+ if (!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* allocate new buffer */
+ if (*out_max_size < (in_size << 2)){
+ if (!(*out_data = tsk_realloc(*out_data, in_size << 2))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = in_size << 2;
+ }
+
+ g722_decode(g722->dec_state, (int16_t*)*out_data, (uint8_t*)in_data, (int)in_size);
+
+ return (in_size << 2);
+}
+
+static tsk_bool_t tdav_codec_g722_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ return tsk_true;
+}
+
+static char* tdav_codec_g722_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ return tsk_null;
+}
+
+//
+// g722 Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g722_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g722_t *g722 = self;
+ if (g722){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g722_dtor(tsk_object_t * self)
+{
+ tdav_codec_g722_t *g722 = self;
+ if (g722){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g722);
+ /* deinit self */
+ if (g722->enc_state){
+ g722_encode_release(g722->enc_state), g722->enc_state = tsk_null;
+ }
+ if (g722->dec_state){
+ g722_decode_release(g722->dec_state), g722->dec_state = tsk_null;
+ }
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g722_def_s =
+{
+ sizeof(tdav_codec_g722_t),
+ tdav_codec_g722_ctor,
+ tdav_codec_g722_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g722_plugin_def_s =
+{
+ &tdav_codec_g722_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_g722,
+ "G722",
+ "g722 Codec (native)",
+ TMEDIA_CODEC_FORMAT_G722,
+ tsk_false,
+ 16000,
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ { 0 },
+
+ tsk_null, // set()
+ tdav_codec_g722_open,
+ tdav_codec_g722_close,
+ tdav_codec_g722_encode,
+ tdav_codec_g722_decode,
+ tdav_codec_g722_sdp_att_match,
+ tdav_codec_g722_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g722_plugin_def_t = &tdav_codec_g722_plugin_def_s;
diff --git a/tinyDAV/src/codecs/g729/tdav_codec_g729.c b/tinyDAV/src/codecs/g729/tdav_codec_g729.c
new file mode 100644
index 0000000..8981687
--- /dev/null
+++ b/tinyDAV/src/codecs/g729/tdav_codec_g729.c
@@ -0,0 +1,466 @@
+/*
+* 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_g729.c
+ * @brief G729ab codec.
+ * Source from: http://www.itu.int/rec/T-REC-G.729-199610-S!AnnB/en
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ */
+#include "tinydav/codecs/g729/tdav_codec_g729.h"
+
+#if HAVE_G729
+
+#include "g729b/dtx.h"
+#include "g729b/octet.h"
+
+#include "tsk_string.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "../thirdparties/win32/lib/g729b/g729b.a")
+#endif
+
+int16_t bad_lsf; /* bad LSF indicator */
+
+#ifndef G729_ENABLE_VAD
+# define G729_ENABLE_VAD 0 // FIXME: speexJB not prepared for such feature
+#endif
+
+static int16_t bin2int(int16_t no_of_bits, const int16_t *bitstream);
+static void int2bin(int16_t value, int16_t no_of_bits, int16_t *bitstream);
+
+static void unpack_G729(const uint8_t bitstream[], int16_t bits[], int len);
+static void unpack_SID(const uint8_t bitstream[], int16_t bits[]);
+
+static void pack_G729(const int16_t ituBits[], uint8_t bitstream[]);
+static void pack_SID(const int16_t ituBits[], uint8_t bitstream[]);
+
+/* ============ G.729ab Plugin interface ================= */
+
+#define tdav_codec_g729ab_set tsk_null
+
+static int tdav_codec_g729ab_open(tmedia_codec_t* self)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+
+ // Initialize the decoder
+ bad_lsf = 0;
+ g729a->decoder.synth = (g729a->decoder.synth_buf + M);
+
+ Init_Decod_ld8a();
+ Init_Post_Filter();
+ Init_Post_Process();
+ /* for G.729B */
+ Init_Dec_cng();
+
+ // Initialize the encoder
+ Init_Pre_Process();
+ Init_Coder_ld8a();
+ Set_zero(g729a->encoder.prm, PRM_SIZE + 1);
+ /* for G.729B */
+ Init_Cod_cng();
+
+
+ return 0;
+}
+
+static int tdav_codec_g729ab_close(tmedia_codec_t* self)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+
+ (void)(g729a);
+
+ /* resources will be freed by the dctor() */
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_g729ab_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t ex_size, out_size = 0;
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+ int i, frame_count = (in_size / 160);
+
+
+ if(!self || !in_data || !in_size || !out_data || (in_size % 160)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ ex_size = (frame_count * 10);
+
+ // allocate new buffer if needed
+ if(*out_max_size <ex_size){
+ if(!(*out_data = tsk_realloc(*out_data, ex_size))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = ex_size;
+ }
+
+ for(i=0; i<frame_count; i++){
+ extern int16_t *new_speech;
+
+ if(g729a->encoder.frame == 32767){
+ g729a->encoder.frame = 256;
+ }
+ else{
+ g729a->encoder.frame++;
+ }
+
+ memcpy(new_speech, &((uint8_t*)in_data)[i*L_FRAME*sizeof(int16_t)], sizeof(int16_t)*L_FRAME);
+
+ Pre_Process(new_speech, L_FRAME);
+ Coder_ld8a(g729a->encoder.prm, g729a->encoder.frame, g729a->encoder.vad_enable);
+ prm2bits_ld8k(g729a->encoder.prm, g729a->encoder.serial);
+
+ if(g729a->encoder.serial[1] == RATE_8000){
+ pack_G729(&g729a->encoder.serial[2], &((uint8_t*)(*out_data))[out_size]);
+ out_size += 10;
+ }
+ else if(g729a->encoder.serial[1] == RATE_SID_OCTET){
+ pack_SID(&g729a->encoder.serial[2], &((uint8_t*)(*out_data))[out_size]);
+ out_size += 2;
+ }
+ else{ // RATE_0
+ //TSK_DEBUG_INFO("G729_RATE_0 - Not transmitted");
+ if (!g729a->encoder.vad_enable) {
+ // silence
+ memset(&((uint8_t*)(*out_data))[out_size], 0, 10);
+ out_size += 10;
+ }
+ }
+ }
+
+ return out_size;
+}
+
+static tsk_size_t tdav_codec_g729ab_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)
+{
+ tsk_size_t out_size = 0;
+ int i, frame_count;
+ const uint8_t* data_start = (const uint8_t*)in_data;
+ const uint8_t* data_end;
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data || ((in_size % 10) && (in_size % 10 != 2))){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ data_end = (data_start + in_size);
+
+ frame_count = (in_size/10) + ((in_size % 10) ? 1 : 0);
+
+ out_size = 160*frame_count;
+
+ /* allocate new buffer if needed */
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ for(i=0; i<frame_count; i++){
+ memset(g729a->decoder.synth_buf, 0, M);
+ g729a->decoder.synth = g729a->decoder.synth_buf + M;
+
+ if((data_end - data_start) == 2){
+ unpack_SID(data_start, g729a->decoder.serial);
+ data_start += 2;
+ }
+ else{
+ unpack_G729(data_start, g729a->decoder.serial, 10);
+ data_start += 10;
+ }
+
+ bits2prm_ld8k(&g729a->decoder.serial[1], g729a->decoder.parm);
+
+ /* This part was modified for version V1.3 */
+ /* for speech and SID frames, the hardware detects frame erasures
+ by checking if all bits are set to zero */
+ /* for untransmitted frames, the hardware detects frame erasures
+ by testing serial[0] */
+
+ g729a->decoder.parm[0] = 0; /* No frame erasure */
+ if(g729a->decoder.serial[1] != 0) {
+ int j;
+ for (j=0; j < g729a->decoder.serial[1]; j++){
+ if (g729a->decoder.serial[j+2] == 0){
+ g729a->decoder.parm[0] = 1; /* frame erased */
+ break;
+ }
+ }
+ }
+ else if(g729a->decoder.serial[0] != SYNC_WORD){
+ g729a->decoder.parm[0] = 1;
+ }
+ if(g729a->decoder.parm[1] == 1) {
+ /* check parity and put 1 in parm[5] if parity error */
+ g729a->decoder.parm[5] = Check_Parity_Pitch(g729a->decoder.parm[4], g729a->decoder.parm[5]);
+ }
+
+ Decod_ld8a(g729a->decoder.parm, g729a->decoder.synth, g729a->decoder.Az_dec, g729a->decoder.T2, &g729a->decoder.Vad);
+ Post_Filter(g729a->decoder.synth, g729a->decoder.Az_dec, g729a->decoder.T2, g729a->decoder.Vad); /* Post-filter */
+ Post_Process(g729a->decoder.synth, L_FRAME);
+
+ memcpy(&((uint8_t*)*out_data)[160*i], g729a->decoder.synth, 160);
+ }
+
+
+ return out_size;
+}
+
+static tsk_bool_t tdav_codec_g729ab_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)codec;
+
+ if(tsk_striequals(att_name, "fmtp")){
+ tsk_params_L_t* params = tsk_null;
+ const char* val_str;
+ if((params = tsk_params_fromstring(att_value, ";", tsk_true))){
+ if((val_str = tsk_params_get_param_value(params, "annexb"))){
+ g729a->encoder.vad_enable &= tsk_strequals(val_str, "yes") ? 1 : 0;
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+ }
+ }
+ return tsk_true;
+}
+
+static char* tdav_codec_g729ab_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ tdav_codec_g729ab_t* g729a = (tdav_codec_g729ab_t*)codec;
+
+ if(tsk_striequals(att_name, "fmtp")){
+ if(g729a->encoder.vad_enable){
+ return tsk_strdup("annexb=yes");
+ }
+ else{
+ return tsk_strdup("annexb=no");
+ }
+ }
+ return tsk_null;
+}
+
+
+
+
+
+
+/* ============ Internal functions ================= */
+
+
+/**
+* Converts from bitstream (ITU bits) to int16_t value
+* @param no_of_bits number of bits to read
+* @param bitstream array containing bits
+* @retval decimal value of bit pattern
+*/
+static int16_t bin2int(int16_t no_of_bits, const int16_t *bitstream)
+{
+ int16_t value, i;
+ int16_t bit;
+
+ value = 0;
+ for(i = 0; i < no_of_bits; i++){
+ value <<= 1;
+ bit = *bitstream++;
+ if (bit == BIT_1){
+ value += 1;
+ }
+ }
+ return(value);
+}
+
+/*----------------------------------------------------------------------------
+ * int2bin convert integer to binary and write the bits bitstream array
+ *----------------------------------------------------------------------------
+ */
+
+/**
+* Writes int16_t value to bitstream
+* @param value decimal value to write
+* @param no_of_bits number of bits from value to write
+* @param bitstream pointer to the destination stream (ITU bits)
+*/
+static void int2bin(int16_t value, int16_t no_of_bits, int16_t *bitstream)
+{
+ int16_t *pt_bitstream;
+ int16_t i, bit;
+
+ pt_bitstream = bitstream + no_of_bits;
+
+ for (i = 0; i < no_of_bits; i++){
+ bit = value & (int16_t)0x0001; /* get lsb */
+ if (bit == 0){
+ *--pt_bitstream = BIT_0;
+ }
+ else{
+ *--pt_bitstream = BIT_1;
+ }
+ value >>= 1;
+ }
+}
+
+/**
+* UnPack RTP bitstream as unpacked ITU stream
+* @param bitstream RTP bitstream to unpack
+* @param bits ITU bitstream used as destination (0 - BIT_0, 1 - BIT_1)
+* @param len length of the RTP bitstream
+*/
+static void unpack_G729(const uint8_t bitstream[], int16_t bits[], int len)
+{
+ int16_t i;
+ *bits++ = SYNC_WORD; /* bit[0], at receiver this bits indicates BFI */
+ switch(len){
+ case 10:
+ *bits++ = SIZE_WORD;
+ break;
+ case 8: // RATE_6400
+ case 15: //RATE_11800
+ default:
+ TSK_DEBUG_ERROR("%d is an invalid lenght value", len);
+ return;
+ }
+
+ for(i=0; i<len; i++){
+ int2bin(bitstream[i], 8, &bits[i*8]);
+ }
+}
+
+/**
+* UnPack RTP bitstream containing SID frame as unpacked ITU stream
+* @param bitstream RTP bitstream to unpack
+* @param bits ITU bitstream used as destination (0 - BIT_0, 1 - BIT_1)
+*/
+static void unpack_SID(const uint8_t bitstream[], int16_t bits[])
+{
+ *bits++ = SYNC_WORD;
+ *bits++ = RATE_SID_OCTET;
+ int2bin((int16_t)bitstream[0], 8, &bits[0]);
+ int2bin((int16_t)bitstream[1], 8, &bits[8]);
+}
+
+/**
+* Pack ITU bits into RTP stream
+* @param ituBits ITU stream to pack (80 shorts)
+* @param bitstream RTP bitstream (80 bits, 5 shorts, 10 bytes)
+*/
+static void pack_G729(const int16_t ituBits[], uint8_t bitstream[])
+{
+ int16_t word16, i;
+ for(i=0; i<5; i++){
+ word16 = bin2int(16, (int16_t*)&ituBits[i*16]);
+ bitstream[i*2] = word16>>8, bitstream[(i*2)+1] = (word16 & 0xFF);
+ }
+}
+
+/**
+* Pack ITU bits containing SID frame as RTP stream
+* @param ituBits ITU stream to pack
+* @param bitstream RTP bitstream (15 bits, 1 short, 2 bytes)
+*/
+static void pack_SID(const int16_t ituBits[], uint8_t bitstream[])
+{
+ int16_t word16 = bin2int(16, ituBits);
+ bitstream[0] = word16>>8, bitstream[1] = (word16 & 0xFF);
+}
+
+
+//
+// g729ab Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_g729ab_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_g729ab_t *g729a = self;
+ if(g729a){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ g729a->encoder.vad_enable = G729_ENABLE_VAD; // AnnexB
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_g729ab_dtor(tsk_object_t * self)
+{
+ tdav_codec_g729ab_t *g729a = self;
+ if(g729a){
+ /* deinit base */
+ tmedia_codec_audio_deinit(g729a);
+ /* deinit self */
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_g729ab_def_s =
+{
+ sizeof(tdav_codec_g729ab_t),
+ tdav_codec_g729ab_ctor,
+ tdav_codec_g729ab_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_g729ab_plugin_def_s =
+{
+ &tdav_codec_g729ab_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_g729ab,
+ "g729",
+ "g729ab Codec (libg729)",
+ TMEDIA_CODEC_FORMAT_G729,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_g729ab_set,
+ tdav_codec_g729ab_open,
+ tdav_codec_g729ab_close,
+ tdav_codec_g729ab_encode,
+ tdav_codec_g729ab_decode,
+ tdav_codec_g729ab_sdp_att_match,
+ tdav_codec_g729ab_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_g729ab_plugin_def_t = &tdav_codec_g729ab_plugin_def_s;
+
+#endif /* HAVE_G729 */
diff --git a/tinyDAV/src/codecs/gsm/tdav_codec_gsm.c b/tinyDAV/src/codecs/gsm/tdav_codec_gsm.c
new file mode 100644
index 0000000..8b5f1bc
--- /dev/null
+++ b/tinyDAV/src/codecs/gsm/tdav_codec_gsm.c
@@ -0,0 +1,209 @@
+/*
+* 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_gsm.c
+ * @brief GSM Full Rate Codec (Based on libgsm)
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/gsm/tdav_codec_gsm.h"
+
+#if HAVE_LIBGSM
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_GSM_FRAME_SIZE 33
+
+/* ============ GSM Plugin interface ================= */
+
+#define tdav_codec_gsm_sdp_att_get tsk_null
+
+int tdav_codec_gsm_open(tmedia_codec_t* self)
+{
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(!gsm->encoder && !(gsm->encoder = gsm_create())){
+ TSK_DEBUG_ERROR("Failed to create GSM encoder");
+ return -2;
+ }
+ if(!gsm->decoder && !(gsm->decoder = gsm_create())){
+ TSK_DEBUG_ERROR("Failed to create GSM decoder");
+ return -3;
+ }
+
+ return 0;
+}
+
+int tdav_codec_gsm_close(tmedia_codec_t* self)
+{
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(gsm->encoder){
+ gsm_destroy(gsm->encoder);
+ gsm->encoder = tsk_null;
+ }
+ if(gsm->decoder){
+ gsm_destroy(gsm->decoder);
+ gsm->decoder = tsk_null;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_gsm_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size;
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = ((in_size / (TMEDIA_CODEC_PCM_FRAME_SIZE_AUDIO_ENCODING(self) * sizeof(short))) * TDAV_GSM_FRAME_SIZE);
+
+ /* allocate new buffer if needed */
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ gsm_encode(gsm->encoder, (gsm_signal*)in_data, (gsm_byte*)*out_data);
+
+ return out_size;
+}
+
+tsk_size_t tdav_codec_gsm_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)
+{
+ tsk_size_t out_size;
+ int ret;
+ tdav_codec_gsm_t* gsm = (tdav_codec_gsm_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data || (in_size % TDAV_GSM_FRAME_SIZE)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ out_size = (in_size / TDAV_GSM_FRAME_SIZE) * (TMEDIA_CODEC_PCM_FRAME_SIZE_AUDIO_DECODING(self) * sizeof(short));
+
+ /* allocate new buffer if needed */
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ ret = gsm_decode(gsm->decoder, (gsm_byte*)in_data, (gsm_signal*)*out_data);
+
+ return out_size;
+}
+
+tsk_bool_t tdav_codec_gsm_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+
+//
+// GSM Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_gsm_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_gsm_t *gsm = self;
+ if(gsm){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_gsm_dtor(tsk_object_t * self)
+{
+ tdav_codec_gsm_t *gsm = self;
+ if(gsm){
+ /* deinit base */
+ tmedia_codec_audio_deinit(gsm);
+ /* deinit self */
+ if(gsm->encoder){
+ gsm_destroy(gsm->encoder);
+ }
+ if(gsm->decoder){
+ gsm_destroy(gsm->decoder);
+ }
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_gsm_def_s =
+{
+ sizeof(tdav_codec_gsm_t),
+ tdav_codec_gsm_ctor,
+ tdav_codec_gsm_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_gsm_plugin_def_s =
+{
+ &tdav_codec_gsm_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_gsm,
+ "GSM",
+ "GSM Full Rate (libgsm)",
+ TMEDIA_CODEC_FORMAT_GSM,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_gsm_open,
+ tdav_codec_gsm_close,
+ tdav_codec_gsm_encode,
+ tdav_codec_gsm_decode,
+ tdav_codec_gsm_sdp_att_match,
+ tdav_codec_gsm_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_gsm_plugin_def_t = &tdav_codec_gsm_plugin_def_s;
+
+
+#endif /* HAVE_LIBGSM */
diff --git a/tinyDAV/src/codecs/h261/tdav_codec_h261.c b/tinyDAV/src/codecs/h261/tdav_codec_h261.c
new file mode 100644
index 0000000..27aaab7
--- /dev/null
+++ b/tinyDAV/src/codecs/h261/tdav_codec_h261.c
@@ -0,0 +1,536 @@
+/*
+* 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_h261.c
+ * @brief H.261 codec plugin.
+ * RTP payloader follows RFC 4587
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/h261/tdav_codec_h261.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tnet_endianness.h"
+
+#include "tsk_string.h"
+#include "tsk_time.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define RTP_PAYLOAD_SIZE 700
+#define H261_HEADER_SIZE 4
+
+static void *run(void* self);
+static void tdav_codec_h261_rtp_callback(tdav_codec_h261_t *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+static void tdav_codec_h261_encap(const tdav_codec_h261_t* h261, const uint8_t* pdata, tsk_size_t size);
+
+/* ============ H.261 Plugin interface ================= */
+
+//
+// H.261 object definition
+//
+int tdav_codec_h261_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+
+ if(!h261){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ //
+ // Encoder
+ //
+ if(!(h261->encoder.codec = avcodec_find_encoder(CODEC_ID_H261))){
+ TSK_DEBUG_ERROR("Failed to find H.261 encoder");
+ return -2;
+ }
+ h261->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h261->encoder.context);
+
+ h261->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h261->encoder.context->time_base.num = 1;
+ h261->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(h261)->out.fps;
+ h261->encoder.context->width = TMEDIA_CODEC_VIDEO(h261)->out.width;
+ h261->encoder.context->height = TMEDIA_CODEC_VIDEO(h261)->out.height;
+
+ /*h261->encoder.context->mb_qmin =*/ h261->encoder.context->qmin = 4;
+ /*h261->encoder.context->mb_qmax =*/ h261->encoder.context->qmax = 31;
+ h261->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+
+ h261->encoder.context->thread_count = 1;
+ h261->encoder.context->rtp_payload_size = RTP_PAYLOAD_SIZE;
+ h261->encoder.context->opaque = tsk_null;
+ h261->encoder.context->bit_rate = (float) (500000) * 0.80f;
+ h261->encoder.context->bit_rate_tolerance = (int) (500000 * 0.20f);
+ h261->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(h261)->out.fps*4; /* each 4 seconds */
+
+ // Picture (YUV 420)
+ if(!(h261->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h261->encoder.picture);
+ //if((ret = avpicture_alloc((AVPicture*)h261->encoder.picture, PIX_FMT_YUV420P, h261->encoder.context->width, h261->encoder.context->height))){
+ // TSK_DEBUG_ERROR("Failed to allocate encoder picture");
+ // return ret;
+ //}
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h261->encoder.context->width, h261->encoder.context->height);
+ if(!(h261->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(h261->encoder.context, h261->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open H.261 encoder");
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ if(!(h261->decoder.codec = avcodec_find_decoder(CODEC_ID_H261))){
+ TSK_DEBUG_ERROR("Failed to find H.261 decoder");
+ }
+ h261->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(h261->decoder.context);
+
+ h261->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ h261->decoder.context->width = TMEDIA_CODEC_VIDEO(h261)->in.width;
+ h261->decoder.context->height = TMEDIA_CODEC_VIDEO(h261)->in.height;
+
+ // Picture (YUV 420)
+ if(!(h261->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(h261->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, h261->decoder.context->width, h261->decoder.context->height);
+ if(!(h261->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(h261->decoder.context, h261->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open H.261 decoder");
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_h261_close(tmedia_codec_t* self)
+{
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+
+ if(!h261){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ //
+ // Encoder
+ //
+ if(h261->encoder.context){
+ avcodec_close(h261->encoder.context);
+ av_free(h261->encoder.context);
+ h261->encoder.context = tsk_null;
+ }
+ if(h261->encoder.picture){
+ av_free(h261->encoder.picture);
+ }
+ if(h261->encoder.buffer){
+ TSK_FREE(h261->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(h261->decoder.context){
+ avcodec_close(h261->decoder.context);
+ av_free(h261->decoder.context);
+ h261->decoder.context = tsk_null;
+ }
+ if(h261->decoder.picture){
+ av_free(h261->decoder.picture);
+ h261->decoder.picture = tsk_null;
+ }
+ if(h261->decoder.accumulator){
+ TSK_FREE(h261->decoder.accumulator);
+ h261->decoder.accumulator_pos = 0;
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h261_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // delete old buffer
+ if(*out_data){
+ TSK_FREE(*out_data);
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)h261->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, h261->encoder.context->width, h261->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+
+ // Encode data
+ h261->encoder.picture->pts = AV_NOPTS_VALUE;
+ //h261->encoder.picture->pict_type = FF_I_TYPE;
+ ret = avcodec_encode_video(h261->encoder.context, h261->encoder.buffer, size, h261->encoder.picture);
+ if(ret > 0){
+ tdav_codec_h261_encap(h261, h261->encoder.buffer, (tsk_size_t)ret);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_h261_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)
+{
+ uint8_t sbit, ebit;
+ const uint8_t* pdata = in_data;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_h261_t* h261 = (tdav_codec_h261_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || !in_size || !out_data || !h261->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* RFC 4587
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ sbit = *pdata >> 5;
+ ebit = (*pdata >> 2) & 0x07;
+
+ /* Check size */
+ if(in_size < H261_HEADER_SIZE){
+ TSK_DEBUG_ERROR("Too short");
+ return 0;
+ }
+
+ pay_ptr = (pdata + H261_HEADER_SIZE);
+ pay_size = (in_size - H261_HEADER_SIZE);
+ xsize = avpicture_get_size(h261->decoder.context->pix_fmt, h261->decoder.context->width, h261->decoder.context->height);
+
+ /* Packet lost? */
+ if(h261->decoder.last_seq != (rtp_hdr->seq_num - 1) && h261->decoder.last_seq){
+ TSK_DEBUG_INFO("Packet lost");
+ }
+ h261->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((int)(h261->decoder.accumulator_pos + pay_size) <= xsize){
+
+ if((h261->decoder.ebit + sbit) == 8){ /* Perfect one Byte to clean up */
+ if(h261->decoder.accumulator_pos){
+ ((uint8_t*)h261->decoder.accumulator)[h261->decoder.accumulator_pos-1] =
+ (((uint8_t*)h261->decoder.accumulator)[h261->decoder.accumulator_pos-1] & (0xFF << h261->decoder.ebit)) |
+ (*pay_ptr << sbit);
+ }
+ pay_ptr++, pay_size--;
+ }
+ h261->decoder.ebit = ebit;
+
+ memcpy(&((uint8_t*)h261->decoder.accumulator)[h261->decoder.accumulator_pos], pay_ptr, pay_size);
+ h261->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h261->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size <xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ h261->decoder.accumulator_pos = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ /* decode the picture */
+ av_init_packet(&packet);
+ packet.size = (int)h261->decoder.accumulator_pos;
+ packet.data = h261->decoder.accumulator;
+ ret = avcodec_decode_video2(h261->decoder.context, h261->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+ TMEDIA_CODEC_VIDEO(h261)->in.width = h261->decoder.context->width;
+ TMEDIA_CODEC_VIDEO(h261)->in.height = h261->decoder.context->height;
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)h261->decoder.picture, h261->decoder.context->pix_fmt, (int)h261->decoder.context->width, (int)h261->decoder.context->height,
+ *out_data, (int)retsize);
+ }
+ /* in all cases: reset accumulator */
+ h261->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_h261_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ int ret;
+ unsigned maxbr, fps, width, height;
+ tmedia_codec_video_t* h261 = (tmedia_codec_video_t*)codec;
+
+ if(tsk_striequals(att_value, "fmtp")){
+ if(!(ret = tmedia_codec_parse_fmtp(att_value, &maxbr, &fps, &width, &height))){
+ h261->in.max_br = h261->out.max_br = maxbr * 1000;
+ h261->in.fps = h261->out.fps = fps;
+ h261->in.width = h261->out.width = width;
+ h261->in.height = h261->out.height = height;
+ return tsk_true;
+ }
+ else{
+ TSK_DEBUG_WARN("Failed to match fmtp [%s]", att_value);
+ }
+ }
+ return tsk_false;
+}
+
+char* tdav_codec_h261_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+#if 0
+ return tsk_strdup("CIF=2/MaxBR=3840;QCIF=2/MaxBR=1920");
+#else
+ return tsk_strdup("QCIF=2");
+#endif
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h261_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h261_t *h261 = self;
+ if(h261){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h261_dtor(tsk_object_t * self)
+{
+ tdav_codec_h261_t *h261 = self;
+ if(h261){
+ /* deinit base */
+ tmedia_codec_video_deinit(h261); // will call close()
+ /* deinit self */
+ TSK_FREE(h261->rtp.ptr);
+ h261->rtp.size = 0;
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h261_def_s =
+{
+ sizeof(tdav_codec_h261_t),
+ tdav_codec_h261_ctor,
+ tdav_codec_h261_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h261_plugin_def_s =
+{
+ &tdav_codec_h261_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h261,
+ "H261",
+ "H261 codec (FFmpeg)",
+ TMEDIA_CODEC_FORMAT_H261,
+ tsk_false,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tsk_null, // set()
+ tdav_codec_h261_open,
+ tdav_codec_h261_close,
+ tdav_codec_h261_encode,
+ tdav_codec_h261_decode,
+ tdav_codec_h261_sdp_att_match,
+ tdav_codec_h261_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h261_plugin_def_t = &tdav_codec_h261_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ Callbacks ================= */
+
+static void tdav_codec_h261_encap(const tdav_codec_h261_t* h261, const uint8_t* pdata, tsk_size_t size)
+{
+ uint32_t i, last_index = 0;
+
+ if(size < RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+
+ for(i = 4; i<(size - 4); i++){
+ if(pdata[i] == 0x00 && pdata[i+1] == 0x00 && pdata[i+2]>=0x80){ /* PSC or (GBSC) found */
+ if((i - last_index) >= RTP_PAYLOAD_SIZE){
+ tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata+last_index,
+ (i - last_index), (last_index == size));
+ }
+ last_index = i;
+ }
+ }
+last:
+ if(last_index < size - 3/*PSC/GBSC size*/){
+ tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata + last_index,
+ (size - last_index), tsk_true);
+ }
+}
+
+//static void *run(void* self)
+//{
+// uint32_t i, last_index;
+// tsk_list_item_t *curr;
+//
+// const uint8_t* pdata;
+// tsk_size_t size;
+//
+// const tdav_codec_h261_t* h261 = ((tdav_runnable_video_t*)self)->userdata;
+//
+// TSK_DEBUG_INFO("H261 thread === START");
+//
+// TSK_RUNNABLE_RUN_BEGIN(self);
+//
+// if((curr = TSK_RUNNABLE_POP_FIRST(self))){
+// /* 4 is sizeof(uint32_t) */
+// pdata = ((const tsk_buffer_t*)curr->data)->data;
+// size = ((const tsk_buffer_t*)curr->data)->size;
+// last_index = 0;
+//
+// if(size < RTP_PAYLOAD_SIZE){
+// goto last;
+// }
+//
+// for(i = 4; i<(size - 4); i++){
+// if(pdata[i] == 0x00 && pdata[i+1] == 0x00 && pdata[i+2]>=0x80){ /* PSC or (GBSC) found */
+// if((i - last_index) >= RTP_PAYLOAD_SIZE){
+// tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata+last_index,
+// (i - last_index), (last_index == size));
+// }
+// last_index = i;
+// }
+// }
+//last:
+// if(last_index < size - 3/*PSC/GBSC size*/){
+// tdav_codec_h261_rtp_callback((tdav_codec_h261_t*)h261, pdata + last_index,
+// (size - last_index), tsk_true);
+// }
+//
+// tsk_object_unref(curr);
+// }
+//
+// TSK_RUNNABLE_RUN_END(self);
+//
+// TSK_DEBUG_INFO("H261 thread === STOP");
+//
+// return tsk_null;
+//}
+
+static void tdav_codec_h261_rtp_callback(tdav_codec_h261_t *self, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+
+}
+
+tsk_bool_t tdav_codec_ffmpeg_h261_is_supported()
+{
+ return /*(avcodec_find_encoder(CODEC_ID_H261) && avcodec_find_decoder(CODEC_ID_H261))*/tsk_false /* @deprecated */;
+}
+
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/h263/tdav_codec_h263.c b/tinyDAV/src/codecs/h263/tdav_codec_h263.c
new file mode 100644
index 0000000..ed5d77f
--- /dev/null
+++ b/tinyDAV/src/codecs/h263/tdav_codec_h263.c
@@ -0,0 +1,1373 @@
+/*
+* 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_h263.c
+ * @brief H.263-1996 and H.263-1998 codec plugins.
+ * RTP payloader follows RFC 4629 for H263+ and RFC 2190 for H263.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/h263/tdav_codec_h263.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tnet_endianness.h"
+
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_string.h"
+#include "tsk_time.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <libavcodec/avcodec.h>
+
+#define TDAV_H263_GOP_SIZE_IN_SECONDS 25
+#define RTP_PAYLOAD_SIZE 750
+
+#define H263P_HEADER_SIZE 2
+#define H263_HEADER_MODE_A_SIZE 4
+#define H263_HEADER_MODE_B_SIZE 8
+#define H263_HEADER_MODE_C_SIZE 12
+
+#define tdav_codec_h263p_set tdav_codec_h263_set
+#define tdav_codec_h263p_open tdav_codec_h263_open
+#define tdav_codec_h263p_close tdav_codec_h263_close
+#define tdav_codec_h263p_encode tdav_codec_h263_encode
+#define tdav_codec_h263p_sdp_att_match tdav_codec_h263_sdp_att_match
+#define tdav_codec_h263p_sdp_att_get tdav_codec_h263_sdp_att_get
+
+#define tdav_codec_h263pp_set tdav_codec_h263_set
+#define tdav_codec_h263pp_open tdav_codec_h263_open
+#define tdav_codec_h263pp_close tdav_codec_h263_close
+#define tdav_codec_h263pp_encode tdav_codec_h263_encode
+#define tdav_codec_h263pp_decode tdav_codec_h263_decode
+#define tdav_codec_h263pp_sdp_att_match tdav_codec_h263_sdp_att_match
+#define tdav_codec_h263pp_sdp_att_get tdav_codec_h263_sdp_att_get
+
+#define TDAV_CODEC_H263(self) ((tdav_codec_h263_t*)(self))
+
+typedef enum tdav_codec_h263_type_e
+{
+ tdav_codec_h263_1996,
+ tdav_codec_h263_1998,
+ tdav_codec_h263_2000,
+}
+tdav_codec_h263_type_t;
+
+/** H.263-1996 codec */
+typedef struct tdav_codec_h263_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ tdav_codec_h263_type_t type;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+ tsk_bool_t force_idr;
+ int32_t quality; // [1-31]
+ int32_t max_bw_kpbs;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_h263_t;
+
+#define TDAV_DECLARE_CODEC_H263 tdav_codec_h263_t __codec_h263__
+
+static int tdav_codec_h263_init(tdav_codec_h263_t* self, tdav_codec_h263_type_t type, enum CodecID encoder, enum CodecID decoder);
+static int tdav_codec_h263_deinit(tdav_codec_h263_t* self);
+static int tdav_codec_h263_open_encoder(tdav_codec_h263_t* self);
+static int tdav_codec_h263_open_decoder(tdav_codec_h263_t* self);
+static int tdav_codec_h263_close_encoder(tdav_codec_h263_t* self);
+static int tdav_codec_h263_close_decoder(tdav_codec_h263_t* self);
+
+/** H.263-1998 codec */
+typedef struct tdav_codec_h263p_s
+{
+ TDAV_DECLARE_CODEC_H263;
+}
+tdav_codec_h263p_t;
+
+/** H.263-2000 codec */
+typedef struct tdav_codec_h263pp_s
+{
+ TDAV_DECLARE_CODEC_H263;
+}
+tdav_codec_h263pp_t;
+
+
+static void tdav_codec_h263_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+static void tdav_codec_h263p_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t frag, tsk_bool_t marker);
+
+static void tdav_codec_h263_encap(const tdav_codec_h263_t* h263, const uint8_t* pdata, tsk_size_t size);
+
+
+/* ============ Common To all H263 codecs ================= */
+
+static int tdav_codec_h263_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Codec not opened");
+ return -1;
+ }
+ if(param->value_type == tmedia_pvt_int32){
+ if(tsk_striequals(param->key, "action")){
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch(action){
+ case tmedia_codec_action_encode_idr:
+ {
+ h263->encoder.force_idr = tsk_true;
+ break;
+ }
+ case tmedia_codec_action_bw_down:
+ {
+ h263->encoder.quality = TSK_CLAMP(1, (h263->encoder.quality + 1), 31);
+ h263->encoder.context->global_quality = FF_QP2LAMBDA * h263->encoder.quality;
+ break;
+ }
+ case tmedia_codec_action_bw_up:
+ {
+ h263->encoder.quality = TSK_CLAMP(1, (h263->encoder.quality - 1), 31);
+ h263->encoder.context->global_quality = FF_QP2LAMBDA * h263->encoder.quality;
+ break;
+ }
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int tdav_codec_h263_init(tdav_codec_h263_t* self, tdav_codec_h263_type_t type, enum CodecID encoder, enum CodecID decoder)
+{
+ int ret = 0;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->type = type;
+ self->encoder.quality = 1;
+
+ if(!(self->encoder.codec = avcodec_find_encoder(encoder))){
+ TSK_DEBUG_ERROR("Failed to find [%d]encoder", encoder);
+ ret = -2;
+ }
+
+ if(!(self->decoder.codec = avcodec_find_decoder(decoder))){
+ TSK_DEBUG_ERROR("Failed to find [%d] decoder", decoder);
+ ret = -3;
+ }
+
+ self->encoder.max_bw_kpbs = tmedia_defaults_get_bandwidth_video_upload_max();
+
+ /* allocations MUST be done by open() */
+ return ret;
+}
+
+int tdav_codec_h263_deinit(tdav_codec_h263_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->encoder.codec = tsk_null;
+ self->decoder.codec = tsk_null;
+
+ // FFMpeg resources are destroyed by close()
+
+
+
+ TSK_FREE(self->rtp.ptr);
+ self->rtp.size = 0;
+
+ return 0;
+}
+
+
+
+/* ============ H.263-1996 Plugin interface ================= */
+
+//
+// H.263-1996 object definition
+//
+static int tdav_codec_h263_open(tmedia_codec_t* self)
+{
+ int ret;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+
+ if(!h263){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ // Encoder
+ if((ret = tdav_codec_h263_open_encoder(h263))){
+ return ret;
+ }
+
+ // Decoder
+ if((ret = tdav_codec_h263_open_decoder(h263))){
+ return ret;
+ }
+
+ return ret;
+}
+
+static int tdav_codec_h263_close(tmedia_codec_t* self)
+{
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+ int ret;
+
+ if(!h263){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ // Encoder
+ ret = tdav_codec_h263_close_encoder(h263);
+ // Decoder
+ ret = tdav_codec_h263_close_decoder(h263);
+
+ return ret;
+}
+
+static tsk_size_t tdav_codec_h263_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)h263->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, h263->encoder.context->width, h263->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ h263->encoder.picture->pict_type = h263->encoder.force_idr ? FF_I_TYPE : 0;
+#else
+ h263->encoder.picture->pict_type = h263->encoder.force_idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
+#endif
+ h263->encoder.picture->pts = AV_NOPTS_VALUE;
+ h263->encoder.picture->quality = h263->encoder.context->global_quality;
+ ret = avcodec_encode_video(h263->encoder.context, h263->encoder.buffer, size, h263->encoder.picture);
+ if(ret > 0){
+ tdav_codec_h263_encap(h263, h263->encoder.buffer, (tsk_size_t)ret);
+ }
+ h263->encoder.force_idr = tsk_false;
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h263_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)
+{
+ uint8_t F, P, sbit, ebit;
+ const uint8_t* pdata = in_data;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ tsk_size_t hdr_size;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+ tsk_bool_t is_idr = tsk_false;
+
+ if(!self || !in_data || !in_size || !out_data || !h263->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* RFC 2190
+ get F and P bits, used to determine the header Mode (A, B or C)
+ F: 1 bit
+ The flag bit indicates the mode of the payload header. F=0, mode A;
+ F=1, mode B or mode C depending on P bit defined below.
+ P: 1 bit
+ Optional PB-frames mode as defined by the H.263 [4]. "0" implies
+ normal I or P frame, "1" PB-frames. When F=1, P also indicates modes:
+ mode B if P=0, mode C if P=1.
+
+ I: 1 bit.
+ Picture coding type, bit 9 in PTYPE defined by H.263[4], "0" is
+ intra-coded, "1" is inter-coded.
+ */
+ F = *pdata >> 7;
+ P = (*pdata >> 6) & 0x01;
+
+ /* SBIT and EBIT */
+ sbit = (*pdata >> 3) & 0x0F;
+ ebit = (*pdata & 0x07);
+
+ if(F == 0){
+ /* MODE A
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC |I|U|S|A|R |DBQ| TRB | TR |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ hdr_size = H263_HEADER_MODE_A_SIZE;
+ is_idr = (in_size >= 2) && !(pdata[1] & 0x10) /* I==1 */;
+ }
+ else if(P == 0){ // F=1 and P=0
+ /* MODE B
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC | QUANT | GOBN | MBA |R |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |I|U|S|A| HMV1 | VMV1 | HMV2 | VMV2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ hdr_size = H263_HEADER_MODE_B_SIZE;
+ is_idr = (in_size >= 5) && !(pdata[4] & 0x80) /* I==1 */;
+ }
+ else{ // F=1 and P=1
+ /* MODE C
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC | QUANT | GOBN | MBA |R |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |I|U|S|A| HMV1 | VMV1 | HMV2 | VMV2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RR |DBQ| TRB | TR |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ hdr_size = H263_HEADER_MODE_C_SIZE;
+ is_idr = (in_size >= 5) && !(pdata[4] & 0x80) /* I==1 */;
+ }
+
+ /* Check size */
+ if(in_size < hdr_size){
+ TSK_DEBUG_ERROR("Too short");
+ return 0;
+ }
+
+ pay_ptr = (pdata + hdr_size);
+ pay_size = (in_size - hdr_size);
+ xsize = avpicture_get_size(h263->decoder.context->pix_fmt, h263->decoder.context->width, h263->decoder.context->height);
+
+ /* Packet lost? */
+ if(h263->decoder.last_seq != (rtp_hdr->seq_num - 1) && h263->decoder.last_seq){
+ if(h263->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("[H.263] Packet loss, seq_num=%d", rtp_hdr->seq_num);
+ }
+ h263->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((int)(h263->decoder.accumulator_pos + pay_size) <= xsize){
+ if((h263->decoder.ebit + sbit) == 8){ /* Perfect one Byte to clean up */
+ if(h263->decoder.accumulator_pos){
+ ((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos-1] = (((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos-1] & (0xFF << h263->decoder.ebit)) |
+ (*pay_ptr & (0xFF >> sbit));
+ }
+ pay_ptr++, pay_size--;
+ }
+ h263->decoder.ebit = ebit;
+
+ memcpy(&((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos], pay_ptr, pay_size);
+ h263->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h263->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size <xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ h263->decoder.accumulator_pos = 0;
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ av_init_packet(&packet);
+ packet.size = (int)h263->decoder.accumulator_pos;
+ packet.data = h263->decoder.accumulator;
+ ret = avcodec_decode_video2(h263->decoder.context, h263->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret < 0){
+ TSK_DEBUG_WARN("Failed to decode the buffer with error code = %d", ret);
+ if(TMEDIA_CODEC_VIDEO(self)->in.callback){
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+ else if(got_picture_ptr){
+ retsize = xsize;
+ // Is it IDR frame?
+ if(is_idr && TMEDIA_CODEC_VIDEO(self)->in.callback){
+ TSK_DEBUG_INFO("Decoded H.263 IDR");
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ TMEDIA_CODEC_VIDEO(h263)->in.width = h263->decoder.context->width;
+ TMEDIA_CODEC_VIDEO(h263)->in.height = h263->decoder.context->height;
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)h263->decoder.picture, h263->decoder.context->pix_fmt, (int)h263->decoder.context->width, (int)h263->decoder.context->height,
+ *out_data, (int)retsize);
+ }
+ /* in all cases: reset accumulator */
+ h263->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+static tsk_bool_t tdav_codec_h263_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ if(tsk_striequals(att_name, "fmtp")){
+ unsigned width, height, fps;
+ if(tmedia_parse_video_fmtp(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &width, &height, &fps)){
+ TSK_DEBUG_ERROR("Failed to match fmtp=%s", att_value);
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = TMEDIA_CODEC_VIDEO(codec)->out.width = width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = TMEDIA_CODEC_VIDEO(codec)->out.height = height;
+ TMEDIA_CODEC_VIDEO(codec)->in.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps = fps;
+ }
+#if 0
+ else if(tsk_striequals(att_name, "imageattr")){
+ unsigned in_width, in_height, out_width, out_height;
+ if(tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0){
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = in_width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = in_height;
+ TMEDIA_CODEC_VIDEO(codec)->out.width = out_width;
+ TMEDIA_CODEC_VIDEO(codec)->out.height = out_height;
+ }
+#endif
+
+ return tsk_true;
+}
+
+static char* tdav_codec_h263_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ if(tsk_striequals(att_name, "fmtp")){
+ tmedia_pref_video_size_t cif_vs;
+ if(tmedia_video_get_closest_cif_size(TMEDIA_CODEC_VIDEO(codec)->pref_size, &cif_vs)){
+ TSK_DEBUG_ERROR("Failed to get closest CIF family size");
+ return tsk_null;
+ }
+ return tmedia_get_video_fmtp(cif_vs);
+ }
+#if 0
+ else if(tsk_striequals(att_name, "imageattr")){
+ return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(codec)->pref_size,
+ TMEDIA_CODEC_VIDEO(codec)->in.width, TMEDIA_CODEC_VIDEO(codec)->in.height, TMEDIA_CODEC_VIDEO(codec)->out.width, TMEDIA_CODEC_VIDEO(codec)->out.height);
+ }
+#endif
+ return tsk_null;
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h263_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h263_t *h263 = self;
+ if(h263){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h263_init(TDAV_CODEC_H263(self), tdav_codec_h263_1996, CODEC_ID_H263, CODEC_ID_H263);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h263_dtor(tsk_object_t * self)
+{
+ tdav_codec_h263_t *h263 = self;
+ if(h263){
+ /* deinit base */
+ tmedia_codec_video_deinit(h263);
+ /* deinit self */
+ tdav_codec_h263_deinit(TDAV_CODEC_H263(self));
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h263_def_s =
+{
+ sizeof(tdav_codec_h263_t),
+ tdav_codec_h263_ctor,
+ tdav_codec_h263_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h263_plugin_def_s =
+{
+ &tdav_codec_h263_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h263,
+ "H263",
+ "H263-1996 codec (FFmpeg)",
+ TMEDIA_CODEC_FORMAT_H263,
+ tsk_false,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h263_set,
+ tdav_codec_h263_open,
+ tdav_codec_h263_close,
+ tdav_codec_h263_encode,
+ tdav_codec_h263_decode,
+ tdav_codec_h263_sdp_att_match,
+ tdav_codec_h263_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h263_plugin_def_t = &tdav_codec_h263_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ H.263-1998 Plugin interface ================= */
+
+//
+// H.263-1998 object definition
+//
+
+static tsk_size_t tdav_codec_h263p_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)
+{
+ uint8_t P, V, PLEN, PEBIT;
+ uint8_t* pdata = (uint8_t*)in_data;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ int hdr_size = H263P_HEADER_SIZE;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_h263_t* h263 = (tdav_codec_h263_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || !in_size || ((int)in_size <= hdr_size) || !out_data || !h263->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+/*
+ rfc4629 - 5.1. General H.263+ Payload Header
+
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RR |P|V| PLEN |PEBIT|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+ P = (pdata[0] & 0x04)>>2;
+ V = (pdata[0] & 0x02)>>1;
+ PLEN = (((pdata[0] & 0x01)<<5) | pdata[1]>>3);
+ PEBIT = pdata[1] & 0x07;
+
+ if(V){
+ /*
+ Indicates the presence of an 8-bit field containing information
+ for Video Redundancy Coding (VRC), which follows immediately after
+ the initial 16 bits of the payload header, if present. For syntax
+ and semantics of that 8-bit VRC field, see Section 5.2.
+ */
+ }
+ if(PLEN){
+ /*
+ Length, in bytes, of the extra picture header. If no extra
+ picture header is attached, PLEN is 0. If PLEN>0, the extra
+ picture header is attached immediately following the rest of the
+ payload header. Note that the length reflects the omission of the
+ first two bytes of the picture start code (PSC). See Section 6.1.
+ */
+ hdr_size += PLEN;
+ if(PEBIT){
+ /*
+ Indicates the number of bits that shall be ignored in the last
+ byte of the picture header. If PLEN is not zero, the ignored bits
+ shall be the least significant bits of the byte. If PLEN is zero,
+ then PEBIT shall also be zero.
+ */
+ TSK_DEBUG_WARN("PEBIT ignored");
+ }
+ }
+ if(P){ /* MUST be done after PLEN and PEBIT */
+ /*
+ Indicates the picture start or a picture segment (GOB/Slice) start
+ or a video sequence end (EOS or EOSBS). Two bytes of zero bits
+ then have to be prefixed to the payload of such a packet to
+ compose a complete picture/GOB/slice/EOS/EOSBS start code. This
+ bit allows the omission of the two first bytes of the start codes,
+ thus improving the compression ratio.
+ */
+ hdr_size -= 2;
+ pdata[hdr_size] = 0x00, pdata[hdr_size + 1] = 0x00;
+ }
+
+ pay_ptr = (pdata + hdr_size);
+ pay_size = (in_size - hdr_size);
+ xsize = avpicture_get_size(h263->decoder.context->pix_fmt, h263->decoder.context->width, h263->decoder.context->height);
+
+ /* Packet lost? */
+ if(h263->decoder.last_seq != (rtp_hdr->seq_num - 1) && h263->decoder.last_seq){
+ if(h263->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("[H.263+] Packet loss, seq_num=%d", rtp_hdr->seq_num);
+ }
+ h263->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((int)(h263->decoder.accumulator_pos + pay_size) <= xsize){
+ /* PEBIT is ignored */
+ memcpy(&((uint8_t*)h263->decoder.accumulator)[h263->decoder.accumulator_pos], pay_ptr, pay_size);
+ h263->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ h263->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size < xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ h263->decoder.accumulator_pos = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ /* decode the picture */
+ av_init_packet(&packet);
+ packet.size = (int)h263->decoder.accumulator_pos;
+ packet.data = h263->decoder.accumulator;
+ ret = avcodec_decode_video2(h263->decoder.context, h263->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+ TMEDIA_CODEC_VIDEO(h263)->in.width = h263->decoder.context->width;
+ TMEDIA_CODEC_VIDEO(h263)->in.height = h263->decoder.context->height;
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)h263->decoder.picture, h263->decoder.context->pix_fmt, (int)h263->decoder.context->width, (int)h263->decoder.context->height,
+ *out_data, (int)retsize);
+ }
+ /* in all cases: reset accumulator */
+ h263->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+/* constructor */
+static tsk_object_t* tdav_codec_h263p_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h263p_t *h263p = self;
+ if(h263p){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h263_init(TDAV_CODEC_H263(self), tdav_codec_h263_1998, CODEC_ID_H263P, CODEC_ID_H263);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h263p_dtor(tsk_object_t * self)
+{
+ tdav_codec_h263p_t *h263p = self;
+ if(h263p){
+ /* deinit base */
+ tmedia_codec_video_deinit(h263p);
+ /* deinit self */
+ tdav_codec_h263_deinit(TDAV_CODEC_H263(self));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h263p_def_s =
+{
+ sizeof(tdav_codec_h263p_t),
+ tdav_codec_h263p_ctor,
+ tdav_codec_h263p_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h263p_plugin_def_s =
+{
+ &tdav_codec_h263p_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h263p,
+ "H263-1998",
+ "H263-1998 codec (FFmpeg)",
+ TMEDIA_CODEC_FORMAT_H263_1998,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps) */
+ {176, 144, 0},// fps is @deprecated
+
+ tdav_codec_h263p_set,
+ tdav_codec_h263p_open,
+ tdav_codec_h263p_close,
+ tdav_codec_h263p_encode,
+ tdav_codec_h263p_decode,
+ tdav_codec_h263p_sdp_att_match,
+ tdav_codec_h263p_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h263p_plugin_def_t = &tdav_codec_h263p_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ H.263-2000 Plugin interface ================= */
+
+//
+// H.263-2000 object definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_h263pp_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h263pp_t *h263pp = self;
+ if(h263pp){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h263_init(TDAV_CODEC_H263(self), tdav_codec_h263_2000, CODEC_ID_H263P, CODEC_ID_H263);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h263pp_dtor(tsk_object_t * self)
+{
+ tdav_codec_h263pp_t *h263pp = self;
+ if(h263pp){
+ /* deinit base */
+ tmedia_codec_video_deinit(h263pp);
+ /* deinit self */
+ tdav_codec_h263_deinit(TDAV_CODEC_H263(self));
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h263pp_def_s =
+{
+ sizeof(tdav_codec_h263pp_t),
+ tdav_codec_h263pp_ctor,
+ tdav_codec_h263pp_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h263pp_plugin_def_s =
+{
+ &tdav_codec_h263pp_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h263pp,
+ "H263-2000",
+ "H263-2000 codec (FFmpeg)",
+ TMEDIA_CODEC_FORMAT_H263_2000,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps)*/
+ {176, 144, 0},// fps is @deprecated
+
+ tdav_codec_h263pp_set,
+ tdav_codec_h263pp_open,
+ tdav_codec_h263pp_close,
+ tdav_codec_h263pp_encode,
+ tdav_codec_h263pp_decode,
+ tdav_codec_h263pp_sdp_att_match,
+ tdav_codec_h263pp_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h263pp_plugin_def_t = &tdav_codec_h263pp_plugin_def_s;
+
+
+
+int tdav_codec_h263_open_encoder(tdav_codec_h263_t* self)
+{
+ int ret;
+ int size;
+ int32_t max_bw_kpbs;
+ if(self->encoder.context){
+ TSK_DEBUG_ERROR("Encoder already opened");
+ return -1;
+ }
+
+ self->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->encoder.context);
+
+ self->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->encoder.context->time_base.num = 1;
+ self->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(self)->out.fps;
+ self->encoder.context->width = TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.context->height = TMEDIA_CODEC_VIDEO(self)->out.height;
+
+ self->encoder.context->qmin = 10;
+ self->encoder.context->qmax = 51;
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ self->encoder.context->mb_qmin = self->encoder.context->qmin;
+ self->encoder.context->mb_qmax = self->encoder.context->qmax;
+#endif
+ self->encoder.context->mb_decision = FF_MB_DECISION_RD;
+ max_bw_kpbs = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
+ self->encoder.max_bw_kpbs
+ );
+ self->encoder.context->bit_rate = (max_bw_kpbs * 1024);// bps
+ //self->encoder.context->rc_lookahead = 0;
+ self->encoder.context->rtp_payload_size = RTP_PAYLOAD_SIZE;
+ self->encoder.context->opaque = tsk_null;
+ self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->out.fps * TDAV_H263_GOP_SIZE_IN_SECONDS);
+ self->encoder.context->flags |= CODEC_FLAG_QSCALE;
+ self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality;
+ self->encoder.context->max_b_frames = 0;
+
+ // Picture (YUV 420)
+ if(!(self->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->encoder.picture);
+ //if((ret = avpicture_alloc((AVPicture*)self->encoder.picture, PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height))){
+ // TSK_DEBUG_ERROR("Failed to allocate encoder picture");
+ // return ret;
+ //}
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height);
+ if(!(self->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+
+ // RTP Callback
+ switch(self->type){
+ case tdav_codec_h263_1996:
+ { // H263 - 1996
+ break;
+ }
+ case tdav_codec_h263_1998:
+ { // H263 - 1998
+#if defined(CODEC_FLAG_H263P_UMV)
+ self->encoder.context->flags |= CODEC_FLAG_H263P_UMV; // Annex D+
+#endif
+ self->encoder.context->flags |= CODEC_FLAG_AC_PRED; // Annex I and T
+ self->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER; // Annex J
+#if defined(CODEC_FLAG_H263P_SLICE_STRUCT)
+ self->encoder.context->flags |= CODEC_FLAG_H263P_SLICE_STRUCT; // Annex K
+#endif
+#if defined(CODEC_FLAG_H263P_AIV)
+ self->encoder.context->flags |= CODEC_FLAG_H263P_AIV; // Annex S
+#endif
+ break;
+ }
+ case tdav_codec_h263_2000:
+ { // H263 - 2000
+#if defined(CODEC_FLAG_H263P_UMV)
+ self->encoder.context->flags |= CODEC_FLAG_H263P_UMV; // Annex D+
+#endif
+ self->encoder.context->flags |= CODEC_FLAG_AC_PRED; // Annex I and T
+ self->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER; // Annex J
+#if defined(CODEC_FLAG_H263P_SLICE_STRUCT)
+ self->encoder.context->flags |= CODEC_FLAG_H263P_SLICE_STRUCT; // Annex K
+#endif
+#if defined(CODEC_FLAG_H263P_AIV)
+ self->encoder.context->flags |= CODEC_FLAG_H263P_AIV; // Annex S
+#endif
+ break;
+ }
+ }
+ // Open encoder
+ if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc);
+ return ret;
+ }
+
+ TSK_DEBUG_INFO("[H.263] bitrate=%d bps", self->encoder.context->bit_rate);
+
+ return ret;
+}
+
+int tdav_codec_h263_open_decoder(tdav_codec_h263_t* self)
+{
+ int ret, size;
+
+ if(self->decoder.context){
+ TSK_DEBUG_ERROR("Decoder already opened");
+ return -1;
+ }
+
+ self->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->decoder.context);
+
+ self->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->decoder.context->width = TMEDIA_CODEC_VIDEO(self)->in.width;
+ self->decoder.context->height = TMEDIA_CODEC_VIDEO(self)->in.height;
+
+ // Picture (YUV 420)
+ if(!(self->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->decoder.context->width, self->decoder.context->height);
+ if(!(self->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(self->decoder.context, self->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc);
+ return ret;
+ }
+
+ self->decoder.last_seq = 0;
+
+ return ret;
+}
+
+int tdav_codec_h263_close_encoder(tdav_codec_h263_t* self)
+{
+ if(self->encoder.context){
+ avcodec_close(self->encoder.context);
+ av_free(self->encoder.context);
+ self->encoder.context = tsk_null;
+ }
+ if(self->encoder.picture){
+ av_free(self->encoder.picture);
+ self->encoder.picture = tsk_null;
+ }
+ if(self->encoder.buffer){
+ TSK_FREE(self->encoder.buffer);
+ }
+ return 0;
+}
+
+int tdav_codec_h263_close_decoder(tdav_codec_h263_t* self)
+{
+ if(self->decoder.context){
+ avcodec_close(self->decoder.context);
+ av_free(self->decoder.context);
+ self->decoder.context = tsk_null;
+ }
+ if(self->decoder.picture){
+ av_free(self->decoder.picture);
+ self->decoder.picture = tsk_null;
+ }
+ if(self->decoder.accumulator){
+ TSK_FREE(self->decoder.accumulator);
+ self->decoder.accumulator_pos = 0;
+ }
+ return 0;
+}
+
+/* ============ Callbacks ================= */
+
+static void tdav_codec_h263_encap(const tdav_codec_h263_t* h263, const uint8_t* pdata, tsk_size_t size)
+{
+ tsk_bool_t frag = tsk_false;
+ register uint32_t i, last_index = 0;
+
+ if(size < RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+
+ for(i = 4; i<(size - 4); i++){
+ if(pdata[i] == 0x00 && pdata[i+1] == 0x00 && pdata[i+2]>=0x80){ /* PSC or (GBSC) found */
+ if((i - last_index) >= RTP_PAYLOAD_SIZE || tsk_true/* FIXME */){
+ switch(h263->type){
+ case tdav_codec_h263_1996:
+ tdav_codec_h263_rtp_callback((tdav_codec_h263_t*) h263, pdata+last_index,
+ (i - last_index), (last_index == size));
+ break;
+ default:
+ tdav_codec_h263p_rtp_callback((tdav_codec_h263_t*) h263, pdata + last_index,
+ (i - last_index), frag, (last_index == size));
+ frag = tsk_true;
+ break;
+ }
+ last_index = i;
+ }
+ }
+ }
+last:
+ if(last_index < size){
+ switch(h263->type){
+ case tdav_codec_h263_1996:
+ tdav_codec_h263_rtp_callback((tdav_codec_h263_t*) h263, pdata + last_index,
+ (size - last_index), tsk_true);
+ break;
+ default:
+ tdav_codec_h263p_rtp_callback((tdav_codec_h263_t*) h263, pdata + last_index,
+ (size - last_index), frag, tsk_true);
+ break;
+ }
+ }
+}
+
+
+static void tdav_codec_h263_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+ uint8_t* pdata = (uint8_t*)data;
+
+ if(self->rtp.size < (size + H263_HEADER_MODE_A_SIZE)){
+ if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (size + H263_HEADER_MODE_A_SIZE)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return;
+ }
+ self->rtp.size = (size + H263_HEADER_MODE_A_SIZE);
+ }
+ memcpy((self->rtp.ptr + H263_HEADER_MODE_A_SIZE), data, size);
+
+ /* http://eu.sabotage.org/www/ITU/H/H0263e.pdf section 5.1
+ * 5.1.1 Picture Start Code (PSC) (22 bits) - PSC is a word of 22 bits. Its value is 0000 0000 0000 0000 1 00000.
+
+ *
+ * 5.1.1 Picture Start Code (PSC) (22 bits)
+ * 5.1.2 Temporal Reference (TR) (8 bits)
+ * 5.1.3 Type Information (PTYPE) (Variable Length)
+ * – Bit 1: Always "1", in order to avoid start code emulation.
+ * – Bit 2: Always "0", for distinction with Recommendation H.261.
+
+ * – Bit 3: Split screen indicator, "0" off, "1" on.
+ * – Bit 4: Document camera indicator, "0" off, "1" on.
+ * – Bit 5: Full Picture Freeze Release, "0" off, "1" on.
+ * – Bits 6-8: Source Format, "000" forbidden, "001" sub-QCIF, "010" QCIF, "011" CIF,
+ "100" 4CIF, "101" 16CIF, "110" reserved, "111" extended PTYPE.
+ If bits 6-8 are not equal to "111", which indicates an extended PTYPE (PLUSPTYPE), the following
+ five bits are also present in PTYPE:
+ – Bit 9: Picture Coding Type, "0" INTRA (I-picture), "1" INTER (P-picture).
+ – Bit 10: Optional Unrestricted Motion Vector mode (see Annex D), "0" off, "1" on.
+ – Bit 11: Optional Syntax-based Arithmetic Coding mode (see Annex E), "0" off, "1" on.
+ – Bit 12: Optional Advanced Prediction mode (see Annex F), "0" off, "1" on.
+ – Bit 13: Optional PB-frames mode (see Annex G), "0" normal I- or P-picture, "1" PB-frame.
+ */
+ if(pdata[0] == 0x00 && pdata[1] == 0x00 && (pdata[2] & 0xfc)==0x80){ /* PSC */
+ /* RFC 2190 -5.1 Mode A
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|P|SBIT |EBIT | SRC |I|U|S|A|R |DBQ| TRB | TR |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ SRC : 3 bits
+ Source format, bit 6,7 and 8 in PTYPE defined by H.263 [4], specifies
+ the resolution of the current picture.
+
+ I: 1 bit.
+ Picture coding type, bit 9 in PTYPE defined by H.263[4], "0" is
+ intra-coded, "1" is inter-coded.
+ */
+
+ // PDATA[4] ======> Bits 3-10 of PTYPE
+ uint32_t rtp_hdr = 0;
+ uint8_t format, pict_type;
+
+ // Source Format = 4,5,6
+ format = (pdata[4] & 0x3C)>>2;
+ // Picture Coding Type = 7
+ pict_type = (pdata[4] & 0x02)>>1;
+ // RTP mode A header
+ ((uint8_t*)&rtp_hdr)[1] = (format <<5) | (pict_type << 4);
+ //rtp_hdr = tnet_htonl(rtp_hdr);
+ memcpy(self->rtp.ptr, &rtp_hdr, sizeof(rtp_hdr));
+ }
+
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->out.callback){
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->rtp.ptr;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (size + H263_HEADER_MODE_A_SIZE);
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1./(double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = marker;
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
+}
+
+static void tdav_codec_h263p_rtp_callback(tdav_codec_h263_t *self, const void *data, tsk_size_t size, tsk_bool_t frag, tsk_bool_t marker)
+{
+ uint8_t* pdata = (uint8_t*)data;
+ //uint8_t rtp_hdr[2] = {0x00, 0x00};
+ //tsk_bool_t eos = tsk_false;
+
+ const void* _ptr = tsk_null;
+ tsk_size_t _size = 0;
+ //static tsk_bool_t frag = tsk_false;
+ //tsk_bool_t found_gob = tsk_false;
+
+ /* RFC 4629 - 5.1. General H.263+ Payload Header
+ The H.263+ payload header is structured as follows:
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | RR |P|V| PLEN |PEBIT|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ /* http://eu.sabotage.org/www/ITU/H/H0263e.pdf
+ *
+ * 5.1.1 Picture Start Code (PSC) (22 bits)
+ * ->PSC is a word of 22 bits. Its value is 0000 0000 0000 0000 1 00000.
+ * 5.1.27 End Of Sequence (EOS) (22 bits)
+ * ->A codeword of 22 bits. Its value is 0000 0000 0000 0000 1 11111
+ * 5.2.2 Group of Block Start Code (GBSC) (17 bits)
+ * ->A word of 17 bits. Its value is 0000 0000 0000 0000 1
+ * C.4.1 End Of Sub-Bitstream code (EOSBS) (23 bits)
+ * ->The EOSBS code is a codeword of 23 bits. Its value is 0000 0000 0000 0000 1 11110 0
+ *
+ *
+ * 5.2.3 Group Number (GN) (5 bits)
+ * -> last 5 bits
+ */
+ //if(pdata[0] == 0x00 && pdata[1] == 0x00 && pdata[2] >= 0x80){ /* PSC or EOS or GBSC */
+ // uint8_t GN = ((pdata[2]>>2) & 0x1F);
+ // found_gob = tsk_true;
+ // //TSK_DEBUG_INFO("GN=%u", pdata[2]);
+ //
+ // /* RFC 4629 - 6.1.1. Packets that begin with a Picture Start Code
+ // A packet that begins at the location of a Picture, GOB, slice, EOS,
+ // or EOSBS start code shall omit the first two (all zero) bytes from
+ // the H.263+ bitstream and signify their presence by setting P=1 in the
+ // payload header.
+ // */
+
+ // if(GN == 0x00){ /* PSC 00000 */
+ // /* Use the two first bytes as RTP header */
+ // //pdata[0] |= 0x04; // P=1
+
+ // /*
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | RR |1|V|0|0|0|0|0|0|0|0|0| bitstream data without the :
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // : first two 0 bytes of the PSC
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // */
+
+ // //TSK_DEBUG_INFO("H263 - PSC");
+ // }
+ // else if(GN == 0x1F){ /* EOS 11111 */
+ // /* Use the two first bytes as RTP header */
+ // //pdata[0] |= 0x04; // P=1
+ // eos = tsk_true;
+ // /* RFC 4629 - 6.1.3. Packets that begin with an EOS or EOSBS Code
+ // 0 1 2
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | RR |1|V|0|0|0|0|0|0|0|0|0|1|1|1|1|1|1|0|0|
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // */
+ // //TSK_DEBUG_INFO("H263 - EOS");
+ // }
+ // else /*if((GN >> 4) == 0x01)*/{ /* GBSC 10000 */
+ // /* Use the two first bytes as RTP header */
+ // //pdata[0] |= 0x04; // P=1
+ //
+ // /* RFC 4629 - 6.1.2. Packets that begin with GBSC or SSC
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | RR |1|V|0 0 1 0 0 1|PEBIT|1 0 0 0 0 0| picture header :
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // : starting with TR, PTYPE ... |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ... | bitstream :
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // : data starting with GBSC/SSC without its first two 0 bytes
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // */
+ // //TSK_DEBUG_INFO("H263 - GBSC");
+ // found_gob = tsk_false;
+ // }
+ // //else if(EOSBS) -> Not Supported
+ //}
+ //else{
+ // /* 6.2. Encapsulating Follow-on Packet (P=0) */
+ // int i = 0;
+ // i++;
+ //}
+
+ //if(/*eos*/!found_gob && frag){
+ // if(self->rtp.size < (size + 2/* H263+ Header size */)){
+ // if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (size + 2)))){
+ // TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ // return;
+ // }
+ // self->rtp.size = (size + 2);
+ // }
+ // /* RFC 4629 - 6. Packetization Schemes */
+ // //rtp_hdr[0] |= 0x00;
+ // //memcpy(self->rtp.ptr, rtp_hdr/* zeros-> is it corretc? */, 2);
+ // //memcpy((self->rtp.ptr + 2), pdata, size);
+ // //_ptr = self->rtp.ptr;
+ // //_size = (size + 2);
+
+ // pdata[0] |= pdata[2] > 0x80 ? 0x04 : 0x04;
+ // _ptr = pdata;
+ // _size = size;
+ //}
+ //else{
+ // pdata[0] |= pdata[2] > 0x80 ? 0x04 : 0x04;
+ // _ptr = pdata;
+ // _size = size;
+ //}
+
+// FIXME
+ pdata[0] |= pdata[2] > 0x80 ? 0x04 : 0x04;
+ _ptr = pdata;
+ _size = size;
+
+
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->out.callback){
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = _ptr;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = _size;
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1./(double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = marker;
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
+}
+
+tsk_bool_t tdav_codec_ffmpeg_h263_is_supported()
+{
+ return (avcodec_find_encoder(CODEC_ID_H263) && avcodec_find_decoder(CODEC_ID_H263));
+}
+
+tsk_bool_t tdav_codec_ffmpeg_h263p_is_supported()
+{
+ return (avcodec_find_encoder(CODEC_ID_H263P) && avcodec_find_decoder(CODEC_ID_H263));
+}
+
+tsk_bool_t tdav_codec_ffmpeg_h263pp_is_supported()
+{
+ return tdav_codec_ffmpeg_h263p_is_supported();
+}
+
+
+#endif /* HAVE_FFMPEG */
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264.c b/tinyDAV/src/codecs/h264/tdav_codec_h264.c
new file mode 100644
index 0000000..0ec3760
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264.c
@@ -0,0 +1,993 @@
+/*
+* 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_h264.c
+ * @brief H.264 codec plugin using FFmpeg for decoding and x264 for encoding
+ * RTP payloader/depayloader follows RFC 3984
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/h264/tdav_codec_h264.h"
+
+#if HAVE_FFMPEG || HAVE_H264_PASSTHROUGH
+
+#include "tinydav/codecs/h264/tdav_codec_h264_rtp.h"
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#if HAVE_FFMPEG
+# include <libavcodec/avcodec.h>
+# if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0))
+# include <libavutil/opt.h>
+# endif
+#endif
+
+typedef struct tdav_codec_h264_s
+{
+ TDAV_DECLARE_CODEC_H264_COMMON;
+
+ // Encoder
+ struct{
+#if HAVE_FFMPEG
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+#endif
+ void* buffer;
+ int64_t frame_count;
+ tsk_bool_t force_idr;
+ int32_t quality; // [1-31]
+ int rotation;
+ int32_t max_bw_kpbs;
+ tsk_bool_t passthrough; // whether to bypass encoding
+ } encoder;
+
+ // decoder
+ struct{
+#if HAVE_FFMPEG
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+#endif
+ void* accumulator;
+ tsk_size_t accumulator_pos;
+ tsk_size_t accumulator_size;
+ uint16_t last_seq;
+ tsk_bool_t passthrough; // whether to bypass decoding
+ } decoder;
+}
+tdav_codec_h264_t;
+
+#if !defined(TDAV_H264_GOP_SIZE_IN_SECONDS)
+# define TDAV_H264_GOP_SIZE_IN_SECONDS 25
+#endif
+
+#define kResetRotationTrue tsk_true
+#define kResetRotationFalse tsk_false
+
+static int tdav_codec_h264_init(tdav_codec_h264_t* self, profile_idc_t profile);
+static int tdav_codec_h264_deinit(tdav_codec_h264_t* self);
+static int tdav_codec_h264_open_encoder(tdav_codec_h264_t* self);
+static int tdav_codec_h264_close_encoder(tdav_codec_h264_t* self, tsk_bool_t reset_rotation);
+static int tdav_codec_h264_open_decoder(tdav_codec_h264_t* self);
+static int tdav_codec_h264_close_decoder(tdav_codec_h264_t* self);
+
+/* ============ H.264 Base/Main Profile X.X Plugin interface functions ================= */
+
+static int tdav_codec_h264_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+ if (param->value_type == tmedia_pvt_int32) {
+ if(tsk_striequals(param->key, "action")){
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch(action){
+ case tmedia_codec_action_encode_idr:
+ {
+ h264->encoder.force_idr = tsk_true;
+ break;
+ }
+ case tmedia_codec_action_bw_down:
+ {
+ h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality + 1), 31);
+#if HAVE_FFMPEG
+ if (h264->encoder.context) {
+ h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality;
+ }
+#endif
+ break;
+ }
+ case tmedia_codec_action_bw_up:
+ {
+ h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality - 1), 31);
+#if HAVE_FFMPEG
+ if (h264->encoder.context) {
+ h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality;
+ }
+#endif
+ break;
+ }
+ }
+ return 0;
+ }
+ else if(tsk_striequals(param->key, "bw_kbps")){
+ int32_t max_bw_userdefine = self->bandwidth_max_upload;
+ int32_t max_bw_new = *((int32_t*)param->value);
+ if (max_bw_userdefine > 0) {
+ // do not use more than what the user defined in it's configuration
+ h264->encoder.max_bw_kpbs = TSK_MIN(max_bw_new, max_bw_userdefine);
+ }
+ else {
+ h264->encoder.max_bw_kpbs = max_bw_new;
+ }
+ return 0;
+ }
+ else if(tsk_striequals(param->key, "bypass-encoding")){
+ h264->encoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false;
+ TSK_DEBUG_INFO("[H.264] bypass-encoding = %d", h264->encoder.passthrough);
+ return 0;
+ }
+ else if(tsk_striequals(param->key, "bypass-decoding")){
+ h264->decoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false;
+ TSK_DEBUG_INFO("[H.264] bypass-decoding = %d", h264->decoder.passthrough);
+ return 0;
+ }
+ else if(tsk_striequals(param->key, "rotation")){
+ int32_t rotation = *((int32_t*)param->value);
+ if(h264->encoder.rotation != rotation){
+ h264->encoder.rotation = rotation;
+ if (self->opened) {
+ int ret;
+ if ((ret = tdav_codec_h264_close_encoder(h264, kResetRotationFalse))) {
+ return ret;
+ }
+ if ((ret = tdav_codec_h264_open_encoder(h264))) {
+ return ret;
+ }
+#if 0 // Not working
+ if((ret = avcodec_close(h264->encoder.context))){
+ TSK_DEBUG_ERROR("Failed to close [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
+ return ret;
+ }
+ h264->encoder.context->width = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.height : TMEDIA_CODEC_VIDEO(h264)->out.width;
+ h264->encoder.context->height = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.width : TMEDIA_CODEC_VIDEO(h264)->out.height;
+ if((ret = avcodec_open(h264->encoder.context, h264->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
+ return ret;
+ }
+ h264->encoder.force_idr = tsk_true;
+#endif
+ }
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+static int tdav_codec_h264_open(tmedia_codec_t* self)
+{
+ int ret;
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ // Encoder
+ if((ret = tdav_codec_h264_open_encoder(h264))){
+ return ret;
+ }
+
+ // Decoder
+ if((ret = tdav_codec_h264_open_decoder(h264))){
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tdav_codec_h264_close(tmedia_codec_t* self)
+{
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) alreasy checked that the codec is opened */
+
+ // Encoder
+ tdav_codec_h264_close_encoder(h264, kResetRotationTrue);
+
+ // Decoder
+ tdav_codec_h264_close_decoder(h264);
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret = 0;
+
+#if HAVE_FFMPEG
+ int size;
+ tsk_bool_t send_idr, send_hdr;
+#endif
+
+ tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
+
+ if(!self || !in_data || !in_size){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Codec not opened");
+ return 0;
+ }
+
+ if(h264->encoder.passthrough) {
+ tdav_codec_h264_rtp_encap(TDAV_CODEC_H264_COMMON(h264), (const uint8_t*)in_data, in_size);
+ }
+ else { // !h264->encoder.passthrough
+#if HAVE_FFMPEG // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)h264->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, h264->encoder.context->width, h264->encoder.context->height);
+ if (size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size: %u<>%u", size, in_size);
+ return 0;
+ }
+
+ // send IDR for:
+ // - the first frame
+ // - remote peer requested an IDR
+ // - every second within the first 4seconds
+ send_idr = (
+ h264->encoder.frame_count++ == 0
+ || h264 ->encoder.force_idr
+ //|| ( (h264->encoder.frame_count < (int)TMEDIA_CODEC_VIDEO(h264)->out.fps * 4) && ((h264->encoder.frame_count % TMEDIA_CODEC_VIDEO(h264)->out.fps)==0) )
+ );
+
+ // send SPS and PPS headers for:
+ // - IDR frames (not required but it's the easiest way to deal with pkt loss)
+ // - every 5 seconds after the first 4seconds
+ send_hdr = (
+ send_idr
+ //|| ( (h264->encoder.frame_count % (TMEDIA_CODEC_VIDEO(h264)->out.fps * 5))==0 )
+ );
+ if(send_hdr){
+ tdav_codec_h264_rtp_encap(TDAV_CODEC_H264_COMMON(h264), h264->encoder.context->extradata, (tsk_size_t)h264->encoder.context->extradata_size);
+ }
+
+ // Encode data
+ #if LIBAVCODEC_VERSION_MAJOR <= 53
+ h264->encoder.picture->pict_type = send_idr ? FF_I_TYPE : 0;
+ #else
+ h264->encoder.picture->pict_type = send_idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
+ #endif
+ h264->encoder.picture->key_frame = send_idr ? 1 : 0;
+ h264->encoder.picture->pts = AV_NOPTS_VALUE;
+ h264->encoder.picture->quality = h264->encoder.context->global_quality;
+ // h264->encoder.picture->pts = h264->encoder.frame_count; MUST NOT
+ ret = avcodec_encode_video(h264->encoder.context, h264->encoder.buffer, size, h264->encoder.picture);
+ if(ret > 0){
+ tdav_codec_h264_rtp_encap(TDAV_CODEC_H264_COMMON(h264), h264->encoder.buffer, (tsk_size_t)ret);
+ }
+ h264 ->encoder.force_idr = tsk_false;
+#endif
+ }// else(!h264->encoder.passthrough)
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_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_h264_t* h264 = (tdav_codec_h264_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = (const trtp_rtp_header_t*)proto_hdr;
+
+ const uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size = 0;
+ int ret;
+ tsk_bool_t sps_or_pps, append_scp, end_of_unit;
+ tsk_size_t retsize = 0, size_to_copy = 0;
+ static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
+ static tsk_size_t start_code_prefix_size = sizeof(H264_START_CODE_PREFIX);
+#if HAVE_FFMPEG
+ int got_picture_ptr = 0;
+#endif
+
+ if(!h264 || !in_data || !in_size || !out_data
+#if HAVE_FFMPEG
+ || !h264->decoder.context
+#endif
+ )
+ {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ //TSK_DEBUG_INFO("SeqNo=%hu", rtp_hdr->seq_num);
+
+ /* Packet lost? */
+ if((h264->decoder.last_seq + 1) != rtp_hdr->seq_num && h264->decoder.last_seq){
+ TSK_DEBUG_INFO("[H.264] Packet loss, seq_num=%d", (h264->decoder.last_seq + 1));
+ }
+ h264->decoder.last_seq = rtp_hdr->seq_num;
+
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ if(*((uint8_t*)in_data) & 0x80){
+ TSK_DEBUG_WARN("F=1");
+ /* reset accumulator */
+ h264->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ /* get payload */
+ if((ret = tdav_codec_h264_get_pay(in_data, in_size, (const void**)&pay_ptr, &pay_size, &append_scp, &end_of_unit)) || !pay_ptr || !pay_size){
+ TSK_DEBUG_ERROR("Depayloader failed to get H.264 content");
+ return 0;
+ }
+ //append_scp = tsk_true;
+ size_to_copy = pay_size + (append_scp ? start_code_prefix_size : 0);
+ // whether it's SPS or PPS (append_scp is false for subsequent FUA chuncks)
+ sps_or_pps = append_scp && pay_ptr && ((pay_ptr[0] & 0x1F) == 7 || (pay_ptr[0] & 0x1F) == 8);
+
+ // start-accumulator
+ if(!h264->decoder.accumulator){
+ if(size_to_copy > xmax_size){
+ TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", size_to_copy, xmax_size);
+ return 0;
+ }
+ if(!(h264->decoder.accumulator = tsk_calloc(size_to_copy, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocated new buffer");
+ return 0;
+ }
+ h264->decoder.accumulator_size = size_to_copy;
+ }
+ if((h264->decoder.accumulator_pos + size_to_copy) >= xmax_size){
+ TSK_DEBUG_ERROR("BufferOverflow");
+ h264->decoder.accumulator_pos = 0;
+ return 0;
+ }
+ if((h264->decoder.accumulator_pos + size_to_copy) > h264->decoder.accumulator_size){
+ if(!(h264->decoder.accumulator = tsk_realloc(h264->decoder.accumulator, (h264->decoder.accumulator_pos + size_to_copy)))){
+ TSK_DEBUG_ERROR("Failed to reallocated new buffer");
+ h264->decoder.accumulator_pos = 0;
+ h264->decoder.accumulator_size = 0;
+ return 0;
+ }
+ h264->decoder.accumulator_size = (h264->decoder.accumulator_pos + size_to_copy);
+ }
+
+ if(append_scp){
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], H264_START_CODE_PREFIX, start_code_prefix_size);
+ h264->decoder.accumulator_pos += start_code_prefix_size;
+ }
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], pay_ptr, pay_size);
+ h264->decoder.accumulator_pos += pay_size;
+ // end-accumulator
+
+ if(sps_or_pps){
+ // http://libav-users.943685.n4.nabble.com/Decode-H264-streams-how-to-fill-AVCodecContext-from-SPS-PPS-td2484472.html
+ // SPS and PPS should be bundled with IDR
+ TSK_DEBUG_INFO("Receiving SPS or PPS ...to be tied to an IDR");
+ }
+ else if(rtp_hdr->marker){
+ if(h264->decoder.passthrough){
+ if(*out_max_size < h264->decoder.accumulator_pos){
+ if((*out_data = tsk_realloc(*out_data, h264->decoder.accumulator_pos))){
+ *out_max_size = h264->decoder.accumulator_pos;
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+ memcpy(*out_data, h264->decoder.accumulator, h264->decoder.accumulator_pos);
+ retsize = h264->decoder.accumulator_pos;
+ }
+ else { // !h264->decoder.passthrough
+#if HAVE_FFMPEG
+ AVPacket packet;
+
+ /* decode the picture */
+ av_init_packet(&packet);
+ packet.dts = packet.pts = AV_NOPTS_VALUE;
+ packet.size = (int)h264->decoder.accumulator_pos;
+ packet.data = h264->decoder.accumulator;
+ ret = avcodec_decode_video2(h264->decoder.context, h264->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret <0){
+ TSK_DEBUG_INFO("Failed to decode the buffer with error code =%d, size=%u, append=%s", ret, h264->decoder.accumulator_pos, append_scp ? "yes" : "no");
+ if(TMEDIA_CODEC_VIDEO(self)->in.callback){
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+ else if(got_picture_ptr){
+ tsk_size_t xsize;
+
+ /* IDR ? */
+ if(((pay_ptr[0] & 0x1F) == 0x05) && TMEDIA_CODEC_VIDEO(self)->in.callback){
+ TSK_DEBUG_INFO("Decoded H.264 IDR");
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ /* fill out */
+ xsize = avpicture_get_size(h264->decoder.context->pix_fmt, h264->decoder.context->width, h264->decoder.context->height);
+ if(*out_max_size<xsize){
+ if((*out_data = tsk_realloc(*out_data, (xsize + FF_INPUT_BUFFER_PADDING_SIZE)))){
+ *out_max_size = xsize;
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+ retsize = xsize;
+ TMEDIA_CODEC_VIDEO(h264)->in.width = h264->decoder.context->width;
+ TMEDIA_CODEC_VIDEO(h264)->in.height = h264->decoder.context->height;
+ avpicture_layout((AVPicture *)h264->decoder.picture, h264->decoder.context->pix_fmt, (int)h264->decoder.context->width, (int)h264->decoder.context->height,
+ *out_data, (int)retsize);
+ }
+#endif /* HAVE_FFMPEG */
+ } // else(h264->decoder.passthrough)
+
+ h264->decoder.accumulator_pos = 0;
+ } // else if(rtp_hdr->marker)
+
+ return retsize;
+}
+
+static tsk_bool_t tdav_codec_h264_sdp_att_match(const tmedia_codec_t* self, const char* att_name, const char* att_value)
+{
+ return tdav_codec_h264_common_sdp_att_match((tdav_codec_h264_common_t*)self, att_name, att_value);
+}
+
+static char* tdav_codec_h264_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ char* att = tdav_codec_h264_common_sdp_att_get((const tdav_codec_h264_common_t*)self, att_name);
+ if(att && tsk_striequals(att_name, "fmtp")) {
+ tsk_strcat_2(&att, "; impl=%s",
+#if HAVE_FFMPEG
+ "FFMPEG"
+#elif HAVE_H264_PASSTHROUGH
+ "PASSTHROUGH"
+#endif
+ );
+ }
+ return att;
+}
+
+
+
+
+/* ============ H.264 Base Profile Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_base_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_t *h264 = (tdav_codec_h264_t*)self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ if(tdav_codec_h264_init(h264, profile_idc_baseline) != 0){
+ return tsk_null;
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_base_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_t *h264 = (tdav_codec_h264_t*)self;
+ if(h264){
+ /* deinit base */
+ tdav_codec_h264_common_deinit((tdav_codec_h264_common_t*)self);
+ /* deinit self */
+ tdav_codec_h264_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_base_def_s =
+{
+ sizeof(tdav_codec_h264_t),
+ tdav_codec_h264_base_ctor,
+ tdav_codec_h264_base_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_base_plugin_def_s =
+{
+ &tdav_codec_h264_base_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h264_bp,
+ "H264",
+ "H264 Base Profile (FFmpeg, x264)",
+ TMEDIA_CODEC_FORMAT_H264_BP,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps) */
+ {176, 144, 0}, // fps is @deprecated
+
+ tdav_codec_h264_set,
+ tdav_codec_h264_open,
+ tdav_codec_h264_close,
+ tdav_codec_h264_encode,
+ tdav_codec_h264_decode,
+ tdav_codec_h264_sdp_att_match,
+ tdav_codec_h264_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_base_plugin_def_t = &tdav_codec_h264_base_plugin_def_s;
+
+/* ============ H.264 Main Profile Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_main_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_t *h264 = (tdav_codec_h264_t*)self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ if(tdav_codec_h264_init(h264, profile_idc_main) != 0){
+ return tsk_null;
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_main_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_t *h264 = (tdav_codec_h264_t*)self;
+ if(h264){
+ /* deinit base */
+ tdav_codec_h264_common_deinit((tdav_codec_h264_common_t*)self);
+ /* deinit self */
+ tdav_codec_h264_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_main_def_s =
+{
+ sizeof(tdav_codec_h264_t),
+ tdav_codec_h264_main_ctor,
+ tdav_codec_h264_main_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_main_plugin_def_s =
+{
+ &tdav_codec_h264_main_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h264_mp,
+ "H264",
+ "H264 Main Profile (FFmpeg, x264)",
+ TMEDIA_CODEC_FORMAT_H264_MP,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps)*/
+ {176, 144, 0},// fps is @deprecated
+
+ tdav_codec_h264_set,
+ tdav_codec_h264_open,
+ tdav_codec_h264_close,
+ tdav_codec_h264_encode,
+ tdav_codec_h264_decode,
+ tdav_codec_h264_sdp_att_match,
+ tdav_codec_h264_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_main_plugin_def_t = &tdav_codec_h264_main_plugin_def_s;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ============ Common To all H264 codecs ================= */
+
+int tdav_codec_h264_open_encoder(tdav_codec_h264_t* self)
+{
+#if HAVE_FFMPEG
+ int ret;
+ tsk_size_t size;
+
+ if(self->encoder.context){
+ TSK_DEBUG_ERROR("Encoder already opened");
+ return -1;
+ }
+
+#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0))
+ if((self->encoder.context = avcodec_alloc_context3(self->encoder.codec))){
+ avcodec_get_context_defaults3(self->encoder.context, self->encoder.codec);
+ }
+#else
+ if((self->encoder.context = avcodec_alloc_context())){
+ avcodec_get_context_defaults(self->encoder.context);
+ }
+#endif
+
+ if(!self->encoder.context){
+ TSK_DEBUG_ERROR("Failed to allocate context");
+ return -1;
+ }
+
+#if TDAV_UNDER_X86 && LIBAVCODEC_VERSION_MAJOR <= 53
+ self->encoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);
+#endif
+
+ self->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->encoder.context->time_base.num = 1;
+ self->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(self)->out.fps;
+ self->encoder.context->width = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.context->height = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->encoder.max_bw_kpbs = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
+ TMEDIA_CODEC(self)->bandwidth_max_upload
+ );
+ self->encoder.context->bit_rate = (self->encoder.max_bw_kpbs * 1024);// bps
+
+ self->encoder.context->rc_min_rate = (self->encoder.context->bit_rate >> 3);
+ self->encoder.context->rc_max_rate = self->encoder.context->bit_rate;
+
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ self->encoder.context->rc_lookahead = 0;
+#endif
+ self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality;
+
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ self->encoder.context->partitions = X264_PART_I4X4 | X264_PART_I8X8 | X264_PART_P8X8 | X264_PART_B8X8;
+#endif
+ self->encoder.context->me_method = ME_UMH;
+ self->encoder.context->me_range = 16;
+ self->encoder.context->qmin = 10;
+ self->encoder.context->qmax = 51;
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ self->encoder.context->mb_qmin = self->encoder.context->qmin;
+ self->encoder.context->mb_qmax = self->encoder.context->qmax;
+#endif
+ /* METROPOLIS = G2J.COM TelePresence client. Check Issue 378: No video when calling "TANDBERG/4129 (X8.1.1)" */
+#if !METROPOLIS && 0
+ self->encoder.context->flags |= CODEC_FLAG_GLOBAL_HEADER;
+#endif
+ self->encoder.context->flags |= CODEC_FLAG_LOW_DELAY;
+ if (self->encoder.context->profile == FF_PROFILE_H264_BASELINE) {
+ self->encoder.context->max_b_frames = 0;
+ }
+
+ switch(TDAV_CODEC_H264_COMMON(self)->profile){
+ case profile_idc_baseline:
+ default:
+ self->encoder.context->profile = FF_PROFILE_H264_BASELINE;
+ self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level;
+ break;
+ case profile_idc_main:
+ self->encoder.context->profile = FF_PROFILE_H264_MAIN;
+ self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level;
+ break;
+ }
+
+ /* Comment from libavcodec/libx264.c:
+ * Allow x264 to be instructed through AVCodecContext about the maximum
+ * size of the RTP payload. For example, this enables the production of
+ * payload suitable for the H.264 RTP packetization-mode 0 i.e. single
+ * NAL unit per RTP packet.
+ */
+ self->encoder.context->rtp_payload_size = H264_RTP_PAYLOAD_SIZE;
+ self->encoder.context->opaque = tsk_null;
+ self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->out.fps * TDAV_H264_GOP_SIZE_IN_SECONDS);
+
+#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0))
+ if((ret = av_opt_set_int(self->encoder.context->priv_data, "slice-max-size", H264_RTP_PAYLOAD_SIZE, 0))){
+ TSK_DEBUG_ERROR("Failed to set x264 slice-max-size to %d", H264_RTP_PAYLOAD_SIZE);
+ }
+ if((ret = av_opt_set(self->encoder.context->priv_data, "profile", (self->encoder.context->profile == FF_PROFILE_H264_BASELINE ? "baseline" : "main"), 0))){
+ TSK_DEBUG_ERROR("Failed to set x264 profile");
+ }
+ if((ret = av_opt_set(self->encoder.context->priv_data, "preset", "veryfast", 0))){
+ TSK_DEBUG_ERROR("Failed to set x264 preset to veryfast");
+ }
+ if((ret = av_opt_set_int(self->encoder.context->priv_data, "rc-lookahead", 0, 0)) && (ret = av_opt_set_int(self->encoder.context->priv_data, "rc_lookahead", 0, 0))){
+ TSK_DEBUG_ERROR("Failed to set x264 rc_lookahead=0");
+ }
+ if((ret = av_opt_set(self->encoder.context->priv_data, "tune", "animation+zerolatency", 0))){
+ TSK_DEBUG_ERROR("Failed to set x264 tune to zerolatency");
+ }
+#endif
+
+ // Picture (YUV 420)
+ if(!(self->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->encoder.picture);
+
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height);
+ if(!(self->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc);
+ return ret;
+ }
+
+ self->encoder.frame_count = 0;
+
+ TSK_DEBUG_INFO("[H.264] bitrate=%d bps", self->encoder.context->bit_rate);
+
+ return ret;
+#elif HAVE_H264_PASSTHROUGH
+ self->encoder.frame_count = 0;
+ return 0;
+#endif
+
+ TSK_DEBUG_ERROR("Not expected code called");
+ return -1;
+}
+
+int tdav_codec_h264_close_encoder(tdav_codec_h264_t* self, tsk_bool_t reset_rotation)
+{
+#if HAVE_FFMPEG
+ if(self->encoder.context){
+ avcodec_close(self->encoder.context);
+ av_free(self->encoder.context);
+ self->encoder.context = tsk_null;
+ }
+ if(self->encoder.picture){
+ av_free(self->encoder.picture);
+ self->encoder.picture = tsk_null;
+ }
+#endif
+ if(self->encoder.buffer){
+ TSK_FREE(self->encoder.buffer);
+ }
+ self->encoder.frame_count = 0;
+ if (reset_rotation) {
+ self->encoder.rotation = 0; // reset rotation
+ }
+
+ return 0;
+}
+
+int tdav_codec_h264_open_decoder(tdav_codec_h264_t* self)
+{
+#if HAVE_FFMPEG
+ int ret;
+
+ if(self->decoder.context){
+ TSK_DEBUG_ERROR("Decoder already opened");
+ return -1;
+ }
+
+ self->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->decoder.context);
+
+ self->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->decoder.context->flags2 |= CODEC_FLAG2_FAST;
+ self->decoder.context->width = TMEDIA_CODEC_VIDEO(self)->in.width;
+ self->decoder.context->height = TMEDIA_CODEC_VIDEO(self)->in.height;
+
+ // Picture (YUV 420)
+ if(!(self->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->decoder.picture);
+
+ // Open decoder
+ if((ret = avcodec_open(self->decoder.context, self->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(self)->plugin->desc);
+ return ret;
+ }
+ self->decoder.last_seq = 0;
+
+ return ret;
+
+#elif HAVE_H264_PASSTHROUGH
+ return 0;
+#endif
+
+ TSK_DEBUG_ERROR("Unexpected code called");
+ return -1;
+
+}
+
+int tdav_codec_h264_close_decoder(tdav_codec_h264_t* self)
+{
+#if HAVE_FFMPEG
+ if(self->decoder.context){
+ avcodec_close(self->decoder.context);
+ av_free(self->decoder.context);
+ self->decoder.context = tsk_null;
+ }
+ if(self->decoder.picture){
+ av_free(self->decoder.picture);
+ self->decoder.picture = tsk_null;
+ }
+#endif
+ TSK_FREE(self->decoder.accumulator);
+ self->decoder.accumulator_pos = 0;
+
+ return 0;
+}
+
+int tdav_codec_h264_init(tdav_codec_h264_t* self, profile_idc_t profile)
+{
+ int ret = 0;
+ level_idc_t level;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if((ret = tdav_codec_h264_common_init(TDAV_CODEC_H264_COMMON(self)))){
+ TSK_DEBUG_ERROR("tdav_codec_h264_common_init() faile with error code=%d", ret);
+ return ret;
+ }
+
+ if((ret = tdav_codec_h264_common_level_from_size(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, &level))){
+ TSK_DEBUG_ERROR("Failed to find level for size=[%u, %u]", TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height);
+ return ret;
+ }
+
+ (self)->encoder.max_bw_kpbs = TMEDIA_CODEC(self)->bandwidth_max_upload;
+ TDAV_CODEC_H264_COMMON(self)->pack_mode_local = H264_PACKETIZATION_MODE;
+ TDAV_CODEC_H264_COMMON(self)->profile = profile;
+ TDAV_CODEC_H264_COMMON(self)->level = level;
+ TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS*1000;
+ TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR*1000;
+
+#if HAVE_FFMPEG
+ if(!(self->encoder.codec = avcodec_find_encoder(CODEC_ID_H264))){
+ TSK_DEBUG_ERROR("Failed to find H.264 encoder");
+ ret = -2;
+ }
+
+ if(!(self->decoder.codec = avcodec_find_decoder(CODEC_ID_H264))){
+ TSK_DEBUG_ERROR("Failed to find H.264 decoder");
+ ret = -3;
+ }
+#endif
+#if HAVE_H264_PASSTHROUGH
+ TMEDIA_CODEC(self)->passthrough = tsk_true;
+ self->decoder.passthrough = tsk_true;
+ self->encoder.passthrough = tsk_true;
+#endif
+
+ self->encoder.quality = 1;
+
+ /* allocations MUST be done by open() */
+ return ret;
+}
+
+int tdav_codec_h264_deinit(tdav_codec_h264_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+#if HAVE_FFMPEG
+ self->encoder.codec = tsk_null;
+ self->decoder.codec = tsk_null;
+
+ // FFMpeg resources are destroyed by close()
+#endif
+
+ return 0;
+}
+
+tsk_bool_t tdav_codec_ffmpeg_h264_is_supported()
+{
+#if HAVE_FFMPEG
+ return (avcodec_find_encoder(CODEC_ID_H264) && avcodec_find_decoder(CODEC_ID_H264));
+#else
+ return tsk_false;
+#endif
+}
+
+tsk_bool_t tdav_codec_passthrough_h264_is_supported()
+{
+#if HAVE_H264_PASSTHROUGH
+ return tsk_true;
+#else
+ return tsk_false;
+#endif
+}
+
+#endif /* HAVE_FFMPEG || HAVE_H264_PASSTHROUGH */
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264_cisco.cxx b/tinyDAV/src/codecs/h264/tdav_codec_h264_cisco.cxx
new file mode 100644
index 0000000..a501221
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264_cisco.cxx
@@ -0,0 +1,882 @@
+/*
+* Copyright (C) 2014-2015 Mamadou DIOP.
+*
+*
+* 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_h264_cisco.cxx
+ * @brief H.264 codec plugin using OpenH264 (https://github.com/cisco/openh264) v1.1 for encoding/decoding.
+ */
+#include "tinydav/codecs/h264/tdav_codec_h264_cisco.h"
+
+#if HAVE_OPENH264
+
+#include "tinydav/codecs/h264/tdav_codec_h264_common.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tinymedia/tmedia_codec.h"
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_mutex.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+extern "C" {
+#include <wels/codec_api.h>
+#include <wels/codec_app_def.h>
+}
+
+#include <limits.h> /* INT_MAX */
+
+typedef struct tdav_codec_h264_cisco_s
+{
+ TDAV_DECLARE_CODEC_H264_COMMON;
+
+ // Encoder
+ struct{
+ ISVCEncoder *pInst;
+ SEncParamExt sEncParam;
+ SSourcePicture sEncPic;
+ void* buffer;
+ int64_t frame_count;
+ tsk_bool_t force_idr;
+ int rotation;
+ int neg_width;
+ int neg_height;
+ int neg_fps;
+ tsk_mutex_handle_t* mutex;
+ } encoder;
+
+ // decoder
+ struct{
+ ISVCDecoder* pInst;
+ void* accumulator;
+ tsk_size_t accumulator_pos;
+ tsk_size_t accumulator_size;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_h264_cisco_t;
+
+#if !defined(CISCO_H264_GOP_SIZE_IN_SECONDS)
+# define CISCO_H264_GOP_SIZE_IN_SECONDS 25
+#endif
+
+#define kResetRotationTrue tsk_true
+#define kResetRotationFalse tsk_false
+
+static int tdav_codec_h264_cisco_init(tdav_codec_h264_cisco_t* self, profile_idc_t profile);
+static int tdav_codec_h264_cisco_deinit(tdav_codec_h264_cisco_t* self);
+static int tdav_codec_h264_cisco_open_encoder(tdav_codec_h264_cisco_t* self);
+static int tdav_codec_h264_cisco_close_encoder(tdav_codec_h264_cisco_t* self, tsk_bool_t reset_rotation);
+static int tdav_codec_h264_cisco_open_decoder(tdav_codec_h264_cisco_t* self);
+static int tdav_codec_h264_cisco_close_decoder(tdav_codec_h264_cisco_t* self);
+static ELevelIdc tdav_codec_h264_cisco_convert_level(enum level_idc_e level);
+static void tdav_codec_h264_cisco_debug_cb(void* context, int level, const char* message);
+
+static void (*__tdav_codec_h264_cisco_debug_cb)(void* context, int level, const char* message) = tdav_codec_h264_cisco_debug_cb;
+
+/* ============ H.264 Base/Main Profile X.X Plugin interface functions ================= */
+
+static int tdav_codec_h264_cisco_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_h264_cisco_t* h264 = (tdav_codec_h264_cisco_t*)self;
+ tsk_bool_t reconf = tsk_false;
+ if (param->value_type == tmedia_pvt_int32) {
+ if (tsk_striequals(param->key, "action")) {
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch (action) {
+ case tmedia_codec_action_encode_idr:
+ {
+ TSK_DEBUG_INFO("OpenH264 force_idr action");
+ h264->encoder.force_idr = tsk_true;
+ return 0;
+ }
+ case tmedia_codec_action_bw_up:
+ case tmedia_codec_action_bw_down:
+ {
+ int32_t rc_target_bitrate;
+ int32_t bandwidth_max_upload_bps = TMEDIA_CODEC(h264)->bandwidth_max_upload == INT_MAX ? TMEDIA_CODEC(h264)->bandwidth_max_upload : (TMEDIA_CODEC(h264)->bandwidth_max_upload * 1024); // kbps -> bps
+ if (action == tmedia_codec_action_bw_up) {
+ rc_target_bitrate = TSK_CLAMP(0, (int32_t)((h264->encoder.sEncParam.iTargetBitrate * 3) >> 1), bandwidth_max_upload_bps);
+ }
+ else {
+ rc_target_bitrate = TSK_CLAMP(0, (int32_t)((h264->encoder.sEncParam.iTargetBitrate << 1) / 3), bandwidth_max_upload_bps);
+ }
+ h264->encoder.sEncParam.iTargetBitrate = rc_target_bitrate;
+ h264->encoder.sEncParam.iMaxBitrate = rc_target_bitrate;
+ SSpatialLayerConfig* layer = &h264->encoder.sEncParam.sSpatialLayers[0];
+ layer->iMaxSpatialBitrate = h264->encoder.sEncParam.iMaxBitrate;
+ layer->iSpatialBitrate = h264->encoder.sEncParam.iTargetBitrate;
+ reconf = tsk_true;
+ TSK_DEBUG_INFO("OpenH264 new target bitrate = %d bps", rc_target_bitrate);
+ break;
+ }
+ }
+ }
+ else if (tsk_striequals(param->key, "bw_kbps")) { // both up and down (from the SDP)
+ int32_t max_bw_userdefine_kbps = tmedia_defaults_get_bandwidth_video_upload_max();
+ int32_t max_bw_new_kbps = *((int32_t*)param->value);
+ if (max_bw_userdefine_kbps > 0) {
+ // do not use more than what the user defined in it's configuration
+ TMEDIA_CODEC(h264)->bandwidth_max_upload = TSK_MIN(max_bw_new_kbps, max_bw_userdefine_kbps);
+ }
+ else {
+ TMEDIA_CODEC(h264)->bandwidth_max_upload = max_bw_new_kbps;
+ }
+ TSK_DEBUG_INFO("OpenH264 codec: bandwidth-max-upload= %d kbps", TMEDIA_CODEC(h264)->bandwidth_max_upload);
+ reconf = tsk_true;
+ }
+ else if (tsk_striequals(param->key, "bandwidth-max-upload")) {
+ int32_t bw_max_upload_kbps = *((int32_t*)param->value);
+ TSK_DEBUG_INFO("OpenH264 codec: bandwidth-max-upload= %d kbps", bw_max_upload_kbps);
+ TMEDIA_CODEC(h264)->bandwidth_max_upload = bw_max_upload_kbps;
+ reconf = tsk_true;
+ }
+ else if (tsk_striequals(param->key, "rotation")) {
+ int rotation = *((int32_t*)param->value);
+ if (h264->encoder.rotation != rotation) {
+ h264->encoder.rotation = rotation;
+ if (self->opened) {
+ int ret;
+ if ((ret = tdav_codec_h264_cisco_close_encoder(h264, kResetRotationFalse))) {
+ return ret;
+ }
+ if ((ret = tdav_codec_h264_cisco_open_encoder(h264))) {
+ return ret;
+ }
+ }
+ }
+ return 0;
+ }
+ }
+
+ if (reconf) {
+ if (h264->encoder.pInst) {
+ long err;
+ // lock required because of https://code.google.com/p/doubango/issues/detail?id=422
+ tsk_mutex_lock(h264->encoder.mutex);
+ err = h264->encoder.pInst->InitializeExt(&h264->encoder.sEncParam);
+ tsk_mutex_unlock(h264->encoder.mutex);
+ if (err != cmResultSuccess) {
+ TSK_DEBUG_ERROR("InitializeExt failed: %ld", err);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int tdav_codec_h264_cisco_open(tmedia_codec_t* self)
+{
+ int ret;
+ tdav_codec_h264_cisco_t* h264 = (tdav_codec_h264_cisco_t*)self;
+
+ if (!h264) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ // Encoder
+ if ((ret = tdav_codec_h264_cisco_open_encoder(h264))) {
+ return ret;
+ }
+
+ // Decoder
+ if ((ret = tdav_codec_h264_cisco_open_decoder(h264))) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tdav_codec_h264_cisco_close(tmedia_codec_t* self)
+{
+ tdav_codec_h264_cisco_t* h264 = (tdav_codec_h264_cisco_t*)self;
+
+ if (!h264) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ // Encoder
+ tdav_codec_h264_cisco_close_encoder(h264, kResetRotationTrue);
+
+ // Decoder
+ tdav_codec_h264_cisco_close_decoder(h264);
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_cisco_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ long err;
+ tsk_bool_t send_idr, send_hdr;
+ tsk_size_t in_xsize;
+ SFrameBSInfo bsInfo;
+
+ tdav_codec_h264_cisco_t* h264 = (tdav_codec_h264_cisco_t*)self;
+ tdav_codec_h264_common_t* common = (tdav_codec_h264_common_t*)self;
+
+ if (!self || !in_data || !in_size) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if (!self->opened || !h264->encoder.pInst) {
+ TSK_DEBUG_ERROR("Encoder not opened or not ready");
+ return 0;
+ }
+
+ in_xsize = (h264->encoder.sEncPic.iPicHeight * h264->encoder.sEncPic.iPicWidth * 3) >> 1;
+ if (in_xsize != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size: %u<>%u", in_xsize, in_size);
+ return 0;
+ }
+
+ // send IDR for:
+ // - the first frame
+ // - remote peer requested an IDR
+ // - every second within the first 4seconds
+ send_idr = (
+ h264->encoder.frame_count++ == 0
+ || h264 ->encoder.force_idr
+ //|| ( (h264->encoder.frame_count < (int)TMEDIA_CODEC_VIDEO(h264)->out.fps * 4) && ((h264->encoder.frame_count % TMEDIA_CODEC_VIDEO(h264)->out.fps)==0) )
+ );
+
+ // send SPS and PPS headers for:
+ // - IDR frames (not required but it's the easiest way to deal with pkt loss)
+ // - every 5 seconds after the first 4seconds
+ send_hdr = (
+ send_idr
+ //|| ( (h264->encoder.frame_count % (TMEDIA_CODEC_VIDEO(h264)->out.fps * 5))==0 )
+ );
+
+ if (send_idr) {
+ TSK_DEBUG_INFO("OpenH264 call ForceIntraFrame");
+ if ((err = h264->encoder.pInst->ForceIntraFrame(true)) != cmResultSuccess) {
+ TSK_DEBUG_WARN("OpenH264 ForceIntraFrame(%d) failed: %ld", send_idr, err);
+ }
+ }
+ if (send_hdr) {
+#if 0 // Not needed
+ memset(&bsInfo, 0, sizeof(bsInfo));
+ tsk_mutex_lock(h264->encoder.mutex);
+ if ((err = h264->encoder.pInst->EncodeParameterSets(&bsInfo)) != cmResultSuccess) {
+ TSK_DEBUG_WARN("OpenH264 EncodeParameterSets(%d) failed: %ld", send_idr, err);
+ }
+ else {
+ for (int iLayerNum = 0; iLayerNum < bsInfo.iLayerNum; ++iLayerNum) {
+ unsigned char* pBsBuf = bsInfo.sLayerInfo[iLayerNum].pBsBuf;
+ int iNalLengthInByte = 0, _iNalLengthInByte;
+ for (int iNalCount = 0; iNalCount < bsInfo.sLayerInfo[iLayerNum].iNalCount; ++iNalCount) {
+ if ((_iNalLengthInByte = bsInfo.sLayerInfo[iLayerNum].pNalLengthInByte[iNalCount]) > 0) {
+ iNalLengthInByte += _iNalLengthInByte;
+ }
+ }
+ if (iNalLengthInByte > 0) {
+ tdav_codec_h264_rtp_encap(TDAV_CODEC_H264_COMMON(h264), pBsBuf, (tsk_size_t)iNalLengthInByte);
+ }
+ }
+ }
+ tsk_mutex_unlock(h264->encoder.mutex);
+#endif
+ }
+
+ h264->encoder.sEncPic.pData[0] = ((unsigned char*)in_data);
+ h264->encoder.sEncPic.pData[1] = h264->encoder.sEncPic.pData[0] + (h264->encoder.sEncPic.iPicHeight * h264->encoder.sEncPic.iPicWidth);
+ h264->encoder.sEncPic.pData[2] = h264->encoder.sEncPic.pData[1] + ((h264->encoder.sEncPic.iPicHeight * h264->encoder.sEncPic.iPicWidth) >> 2);
+ // h264->encoder.sEncPic.uiTimeStamp = rand();
+
+ memset(&bsInfo, 0, sizeof(bsInfo));
+
+ tsk_mutex_lock(h264->encoder.mutex);
+ if ((err = h264->encoder.pInst->EncodeFrame(&h264->encoder.sEncPic, &bsInfo)) != cmResultSuccess) {
+ TSK_DEBUG_ERROR("OpenH264 setting EncodeFrame() failed: %ld", err);
+ tsk_mutex_unlock(h264->encoder.mutex);
+ return 0;
+ }
+
+ // Memory held by bsInfo is freed when "InitializeExt()" is called this is why the unlock is after reading the output stream
+ if (bsInfo.eFrameType != videoFrameTypeInvalid) {
+ for (int iLayerNum = 0; iLayerNum < bsInfo.iLayerNum; ++iLayerNum) {
+ unsigned char* pBsBuf = bsInfo.sLayerInfo[iLayerNum].pBsBuf;
+ int iNalLengthInByte = 0, _iNalLengthInByte;
+ for (int iNalCount = 0; iNalCount < bsInfo.sLayerInfo[iLayerNum].iNalCount; ++iNalCount) {
+ if ((_iNalLengthInByte = bsInfo.sLayerInfo[iLayerNum].pNalLengthInByte[iNalCount]) > 0) {
+ iNalLengthInByte += _iNalLengthInByte;
+ }
+ }
+ if (iNalLengthInByte > 0) {
+ tdav_codec_h264_rtp_encap(TDAV_CODEC_H264_COMMON(h264), pBsBuf, (tsk_size_t)iNalLengthInByte);
+ }
+ }
+ }
+ tsk_mutex_unlock(h264->encoder.mutex);
+
+ h264 ->encoder.force_idr = tsk_false; // reset
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_cisco_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_h264_cisco_t* h264 = (tdav_codec_h264_cisco_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = (const trtp_rtp_header_t*)proto_hdr;
+
+ const uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size = 0;
+ int ret;
+ long err = cmResultSuccess;
+ tsk_bool_t append_scp, end_of_unit, got_picture_ptr = tsk_false;
+ tsk_bool_t sps_or_pps;
+ tsk_size_t retsize = 0, size_to_copy = 0;
+ static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
+ static tsk_size_t start_code_prefix_size = sizeof(H264_START_CODE_PREFIX);
+
+ if (!h264 || !in_data || !in_size || !out_data) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if (!self->opened || !h264->encoder.pInst) {
+ TSK_DEBUG_ERROR("Decoder not opened or not ready");
+ return 0;
+ }
+
+ /* Packet lost? */
+ if ((h264->decoder.last_seq + 1) != rtp_hdr->seq_num && h264->decoder.last_seq) {
+ TSK_DEBUG_INFO("[H.264] Packet loss, seq_num=%d", (h264->decoder.last_seq + 1));
+ }
+ h264->decoder.last_seq = rtp_hdr->seq_num;
+
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ if (*((uint8_t*)in_data) & 0x80) {
+ TSK_DEBUG_WARN("F=1");
+ /* reset accumulator */
+ h264->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ /* get payload */
+ if ((ret = tdav_codec_h264_get_pay(in_data, in_size, (const void**)&pay_ptr, &pay_size, &append_scp, &end_of_unit)) || !pay_ptr || !pay_size) {
+ TSK_DEBUG_ERROR("Depayloader failed to get H.264 content");
+ return 0;
+ }
+ //append_scp = tsk_true;
+ size_to_copy = pay_size + (append_scp ? start_code_prefix_size : 0);
+ // whether it's SPS or PPS (append_scp is false for subsequent FUA chuncks)
+ sps_or_pps = append_scp && pay_ptr && ((pay_ptr[0] & 0x1F) == 7 || (pay_ptr[0] & 0x1F) == 8);
+
+ // start-accumulator
+ if (!h264->decoder.accumulator) {
+ if (size_to_copy > xmax_size) {
+ TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", size_to_copy, xmax_size);
+ return 0;
+ }
+ if (!(h264->decoder.accumulator = tsk_calloc(size_to_copy, sizeof(uint8_t)))) {
+ TSK_DEBUG_ERROR("Failed to allocated new buffer");
+ return 0;
+ }
+ h264->decoder.accumulator_size = size_to_copy;
+ }
+ if ((h264->decoder.accumulator_pos + size_to_copy) >= xmax_size) {
+ TSK_DEBUG_ERROR("BufferOverflow");
+ h264->decoder.accumulator_pos = 0;
+ return 0;
+ }
+ if ((h264->decoder.accumulator_pos + size_to_copy) > h264->decoder.accumulator_size) {
+ if (!(h264->decoder.accumulator = tsk_realloc(h264->decoder.accumulator, (h264->decoder.accumulator_pos + size_to_copy)))) {
+ TSK_DEBUG_ERROR("Failed to reallocated new buffer");
+ h264->decoder.accumulator_pos = 0;
+ h264->decoder.accumulator_size = 0;
+ return 0;
+ }
+ h264->decoder.accumulator_size = (h264->decoder.accumulator_pos + size_to_copy);
+ }
+
+ if (append_scp) {
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], H264_START_CODE_PREFIX, start_code_prefix_size);
+ h264->decoder.accumulator_pos += start_code_prefix_size;
+ }
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], pay_ptr, pay_size);
+ h264->decoder.accumulator_pos += pay_size;
+ // end-accumulator
+
+ if (/*rtp_hdr->marker*/end_of_unit) {
+ /* decode the picture */
+ unsigned char* out_ptr[3] = { NULL };
+ int out_stride[2] = {0}, out_width = 0, out_height = 0;
+ tsk_size_t out_xsize;
+
+ // Decode a Unit
+ err = h264->decoder.pInst->DecodeFrame(
+ (const unsigned char*)h264->decoder.accumulator, h264->decoder.accumulator_pos,
+ out_ptr, out_stride, out_width, out_height);
+
+ if (err != cmResultSuccess) {
+ if (0 && err == dsDataErrorConcealed) {
+ TSK_DEBUG_INFO("OpenH264: Data error concealed");
+ err = cmResultSuccess;
+ }
+ else {
+ TSK_DEBUG_WARN("OpenH264: DecodeFrame failed: %ld", err);
+ goto bail;
+ }
+ }
+ // Do we have a complete frame?
+ if (!(got_picture_ptr = ((out_ptr[0] && out_ptr[1] && out_ptr[2]) && (out_stride[0] && out_stride[1]) && out_width && out_height))) {
+ goto bail;
+ }
+ out_xsize = (out_width * out_height * 3) >> 1; // I420
+ /* IDR ? */
+ if (((pay_ptr[0] & 0x1F) == 0x05) && TMEDIA_CODEC_VIDEO(self)->in.callback) {
+ TSK_DEBUG_INFO("Decoded H.264 IDR");
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ /* fill out */
+ if (*out_max_size < out_xsize) {
+ if ((*out_data = tsk_realloc(*out_data, out_xsize))){
+ *out_max_size = out_xsize;
+ }
+ else {
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+ TMEDIA_CODEC_VIDEO(h264)->in.width = out_width;
+ TMEDIA_CODEC_VIDEO(h264)->in.height = out_height;
+ /* layout picture */
+ {
+ int plane, y, stride;
+ retsize = 0;
+ for (plane=0; plane < 3; plane++) {
+ unsigned char *buf = out_ptr[plane];
+ stride = out_stride[plane ? 1 : 0];
+ for (y=0; y<out_height >> (plane ? 1 : 0); y++) {
+ unsigned int w_count = out_width >> (plane ? 1 : 0);
+ if ((ret + w_count) > *out_max_size) {
+ TSK_DEBUG_ERROR("BufferOverflow");
+ ret = 0;
+ goto bail;
+ }
+ memcpy(((uint8_t*)*out_data) + retsize, buf, w_count);
+ retsize += w_count;
+ buf += stride;
+ }
+ }
+ }
+ } // else if(rtp_hdr->marker)
+
+bail:
+ /* end of frame */
+ if (got_picture_ptr) {
+ int32_t endOfStream = 1;
+ err = h264->decoder.pInst->SetOption(DECODER_OPTION_END_OF_STREAM, (void*)&endOfStream);
+ if (err != cmResultSuccess) {
+ TSK_DEBUG_WARN("OpenH264 setting DECODER_OPTION_END_OF_STREAM failed: %ld", err);
+ goto bail;
+ }
+ }
+ if (/*rtp_hdr->marker*/end_of_unit) {
+ /* reset accumulator */
+ h264->decoder.accumulator_pos = 0;
+ }
+ if (err != cmResultSuccess){
+ TSK_DEBUG_INFO("Failed to decode the buffer with error code =%ld, size=%u, append=%s", err, h264->decoder.accumulator_pos, append_scp ? "yes" : "no");
+ if (TMEDIA_CODEC_VIDEO(self)->in.callback) {
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+ return retsize;
+}
+
+static tsk_bool_t tdav_codec_h264_cisco_sdp_att_match(const tmedia_codec_t* self, const char* att_name, const char* att_value)
+{
+ return tdav_codec_h264_common_sdp_att_match((tdav_codec_h264_common_t*)self, att_name, att_value);
+}
+
+static char* tdav_codec_h264_cisco_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ char* att = tdav_codec_h264_common_sdp_att_get((const tdav_codec_h264_common_t*)self, att_name);
+ if (att && tsk_striequals(att_name, "fmtp")) {
+ tsk_strcat(&att, "; impl=openh264");
+ }
+ return att;
+}
+
+/* ============ H.264 Base Profile Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_cisco_base_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_cisco_t *h264 = (tdav_codec_h264_cisco_t*)self;
+ if (h264) {
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ if (tdav_codec_h264_cisco_init(h264, profile_idc_baseline) != 0) {
+ return tsk_null;
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_cisco_base_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_cisco_t *h264 = (tdav_codec_h264_cisco_t*)self;
+ if (h264) {
+ /* deinit base */
+ tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
+ /* deinit self */
+ tdav_codec_h264_cisco_deinit(h264);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_cisco_base_def_s =
+{
+ sizeof(tdav_codec_h264_cisco_t),
+ tdav_codec_h264_cisco_base_ctor,
+ tdav_codec_h264_cisco_base_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_cisco_base_plugin_def_s =
+{
+ &tdav_codec_h264_cisco_base_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h264_bp,
+ "H264",
+ "H264 Base Profile (OpenH264)",
+ TMEDIA_CODEC_FORMAT_H264_BP,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps) */
+ {176, 144, 0}, // fps is @deprecated
+
+ tdav_codec_h264_cisco_set,
+ tdav_codec_h264_cisco_open,
+ tdav_codec_h264_cisco_close,
+ tdav_codec_h264_cisco_encode,
+ tdav_codec_h264_cisco_decode,
+ tdav_codec_h264_cisco_sdp_att_match,
+ tdav_codec_h264_cisco_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_cisco_base_plugin_def_t = &tdav_codec_h264_cisco_base_plugin_def_s;
+
+/* ============ Common To all H264 profiles ================= */
+
+static int tdav_codec_h264_cisco_open_encoder(tdav_codec_h264_cisco_t* self)
+{
+ int ret = -1, max_bitrate_bps;
+ long err;
+ SSpatialLayerConfig* layer;
+
+ int32_t max_bw_kpbs;
+ tdav_codec_h264_common_t* common = (tdav_codec_h264_common_t*)self;
+
+ if (self->encoder.pInst) {
+ TSK_DEBUG_ERROR("Encoder already initialized");
+ goto bail;
+ }
+
+ // create encoder
+ if ((err = WelsCreateSVCEncoder(&self->encoder.pInst)) != cmResultSuccess) {
+ TSK_DEBUG_ERROR("Failed to create ancoder: %ld", err);
+ goto bail;
+ }
+
+ self->encoder.pInst->SetOption(ENCODER_OPTION_TRACE_CALLBACK_CONTEXT, self);
+ self->encoder.pInst->SetOption(ENCODER_OPTION_TRACE_CALLBACK, &__tdav_codec_h264_cisco_debug_cb);
+
+ if ((err = self->encoder.pInst->GetDefaultParams(&self->encoder.sEncParam)) != cmResultSuccess) {
+ TSK_DEBUG_ERROR("GetDefaultParams failed: %ld", err);
+ goto bail;
+ }
+
+ self->encoder.neg_width = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.neg_height = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->encoder.neg_fps = TMEDIA_CODEC_VIDEO(self)->out.fps;
+ max_bw_kpbs = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(self->encoder.neg_width, self->encoder.neg_height, self->encoder.neg_fps),
+ TMEDIA_CODEC(self)->bandwidth_max_upload
+ );
+ max_bitrate_bps = (max_bw_kpbs * 1024);
+
+ TSK_DEBUG_INFO("[H.264 OpenH264 Encoder] neg_width=%d, neg_height=%d, neg_fps=%d, max_bitrate_bps=%d",
+ self->encoder.neg_width,
+ self->encoder.neg_height,
+ self->encoder.neg_fps,
+ max_bitrate_bps
+ );
+
+ self->encoder.sEncParam.iInputCsp = videoFormatI420;
+ self->encoder.sEncParam.iSpatialLayerNum = 1;
+ self->encoder.sEncParam.iTemporalLayerNum = 1;
+ self->encoder.sEncParam.uiIntraPeriod = (self->encoder.neg_fps * CISCO_H264_GOP_SIZE_IN_SECONDS);
+ self->encoder.sEncParam.iUsageType = CAMERA_VIDEO_REAL_TIME; // TODO: use "SCREEN_CONTENT_REAL_TIME" screencast
+ self->encoder.sEncParam.iPicWidth = self->encoder.neg_width;
+ self->encoder.sEncParam.iPicHeight = self->encoder.neg_height;
+ self->encoder.sEncParam.iTargetBitrate = max_bitrate_bps;
+ self->encoder.sEncParam.iMaxBitrate = max_bitrate_bps;
+ self->encoder.sEncParam.fMaxFrameRate = (float)self->encoder.neg_fps;
+ self->encoder.sEncParam.uiMaxNalSize = H264_RTP_PAYLOAD_SIZE;
+ self->encoder.sEncParam.bEnableSpsPpsIdAddition = true;
+ self->encoder.sEncParam.bEnableFrameCroppingFlag = true;
+
+ layer = &self->encoder.sEncParam.sSpatialLayers[0];
+ layer->uiProfileIdc = PRO_BASELINE;
+#if BUILD_TYPE_TCH
+ layer->uiLevelIdc = tdav_codec_h264_cisco_convert_level(common->level);
+#else
+ layer->uiLevelIdc = LEVEL_UNKNOWN; // auto-detect
+#endif
+ layer->fFrameRate = self->encoder.sEncParam.fMaxFrameRate;
+ layer->iMaxSpatialBitrate = self->encoder.sEncParam.iMaxBitrate;
+ layer->iSpatialBitrate = self->encoder.sEncParam.iTargetBitrate;
+ layer->iVideoWidth = self->encoder.sEncParam.iPicWidth;
+ layer->iVideoHeight = self->encoder.sEncParam.iPicHeight;
+ layer->sSliceCfg.uiSliceMode = SM_DYN_SLICE;
+ layer->sSliceCfg.sSliceArgument.uiSliceSizeConstraint = H264_RTP_PAYLOAD_SIZE;
+ layer->sSliceCfg.sSliceArgument.uiSliceNum = 1;
+ //layer->sSliceCfg.sSliceArgument.uiSliceMbNum[0] = 960;
+
+ if ((err = self->encoder.pInst->InitializeExt(&self->encoder.sEncParam)) != cmResultSuccess) {
+ TSK_DEBUG_ERROR("InitializeExt failed: %ld", err);
+ goto bail;
+ }
+
+ self->encoder.sEncPic.iColorFormat = videoFormatI420;
+ self->encoder.sEncPic.iPicWidth = self->encoder.sEncParam.iPicWidth;
+ self->encoder.sEncPic.iPicHeight = self->encoder.sEncParam.iPicHeight;
+ self->encoder.sEncPic.iStride[0] = self->encoder.sEncPic.iPicWidth;
+ self->encoder.sEncPic.iStride[1] = self->encoder.sEncPic.iStride[0] >> 1;
+ self->encoder.sEncPic.iStride[2] = self->encoder.sEncPic.iStride[1];
+
+ // Create encoder mutex
+ if (!self->encoder.mutex && !(self->encoder.mutex = tsk_mutex_create())) {
+ TSK_DEBUG_ERROR("Failed to create mutex for the encoder");
+ goto bail;
+ }
+
+ self->encoder.frame_count = 0;
+
+ ret = 0;
+
+bail:
+ return ret;
+}
+
+static int tdav_codec_h264_cisco_close_encoder(tdav_codec_h264_cisco_t* self, tsk_bool_t reset_rotation)
+{
+ if (self) {
+ if (self->encoder.pInst) {
+ self->encoder.pInst->Uninitialize();
+ WelsDestroySVCEncoder(self->encoder.pInst);
+ self->encoder.pInst = NULL;
+ }
+ if (self->encoder.buffer) {
+ TSK_FREE(self->encoder.buffer);
+ }
+ if (self->encoder.mutex) {
+ tsk_mutex_destroy(&self->encoder.mutex);
+ }
+ self->encoder.frame_count = 0;
+ if (reset_rotation) {
+ self->encoder.rotation = 0; // reset rotation
+ }
+ }
+ return 0;
+}
+
+int tdav_codec_h264_cisco_open_decoder(tdav_codec_h264_cisco_t* self)
+{
+ int ret = -1;
+ long err;
+ tdav_codec_h264_common_t* common = (tdav_codec_h264_common_t*)self;
+ SDecodingParam sDecParam = { 0 };
+
+ if (self->decoder.pInst) {
+ TSK_DEBUG_ERROR("Decoder already initialized");
+ goto bail;
+ }
+
+ // create decoder
+ if ((err = WelsCreateDecoder(&self->decoder.pInst)) != cmResultSuccess) {
+ TSK_DEBUG_ERROR("Failed to create decoder: %ld", err);
+ goto bail;
+ }
+ self->decoder.pInst->SetOption(DECODER_OPTION_TRACE_CALLBACK_CONTEXT, self);
+ self->decoder.pInst->SetOption(DECODER_OPTION_TRACE_CALLBACK, &__tdav_codec_h264_cisco_debug_cb);
+
+ // initialize decoder
+ sDecParam.iOutputColorFormat = videoFormatI420;
+ sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;
+ if ((err = self->decoder.pInst->Initialize(&sDecParam)) != cmResultSuccess) {
+ TSK_DEBUG_ERROR("Failed to initialize decoder: %ld", err);
+ goto bail;
+ }
+ self->decoder.last_seq = 0;
+ TSK_DEBUG_INFO("[OpenH264 Decoder] neg_width=%d, neg_height=%d, neg_fps=%d",
+ TMEDIA_CODEC_VIDEO(self)->in.width,
+ TMEDIA_CODEC_VIDEO(self)->in.height,
+ TMEDIA_CODEC_VIDEO(self)->in.fps
+ );
+ ret = 0;
+
+bail:
+ return ret;
+}
+
+static int tdav_codec_h264_cisco_close_decoder(tdav_codec_h264_cisco_t* self)
+{
+ if (self) {
+ if (self->decoder.pInst) {
+ self->decoder.pInst->Uninitialize();
+ WelsDestroyDecoder(self->decoder.pInst);
+ self->decoder.pInst = NULL;
+ }
+ TSK_FREE(self->decoder.accumulator);
+ self->decoder.accumulator_pos = 0;
+ }
+
+ return 0;
+}
+
+static ELevelIdc tdav_codec_h264_cisco_convert_level(enum level_idc_e level)
+{
+ switch(level) {
+ case level_idc_1_0: return LEVEL_1_0;
+ case level_idc_1_b: return LEVEL_1_B;
+ case level_idc_1_1: return LEVEL_1_1;
+ case level_idc_1_2: return LEVEL_1_2;
+ case level_idc_1_3: return LEVEL_1_3;
+ case level_idc_2_0: return LEVEL_2_0;
+ case level_idc_2_1: return LEVEL_2_1;
+ case level_idc_2_2: return LEVEL_2_2;
+ case level_idc_3_0: return LEVEL_3_0;
+ case level_idc_3_1: return LEVEL_3_1;
+ case level_idc_3_2: return LEVEL_3_2;
+ case level_idc_4_0: return LEVEL_4_0;
+ case level_idc_4_1: return LEVEL_4_1;
+ case level_idc_4_2: return LEVEL_4_2;
+ case level_idc_5_0: return LEVEL_5_0;
+ case level_idc_5_1: return LEVEL_5_1;
+ case level_idc_5_2: return LEVEL_2_2;
+ default: return LEVEL_UNKNOWN;
+ }
+}
+
+static void tdav_codec_h264_cisco_debug_cb(void* context, int level, const char* message)
+{
+ switch (level) {
+ case WELS_LOG_ERROR:
+ case WELS_LOG_QUIET:
+ TSK_DEBUG_ERROR("OpenH264: level=%d, message=%s", level, message);
+ break;
+ case WELS_LOG_WARNING:
+ TSK_DEBUG_WARN("OpenH264: level=%d, message=%s", level, message);
+ break;
+ default:
+ TSK_DEBUG_INFO("OpenH264: level=%d, message=%s", level, message);
+ break;
+ }
+}
+
+static int tdav_codec_h264_cisco_init(tdav_codec_h264_cisco_t* self, profile_idc_t profile)
+{
+ int ret = -1;
+ level_idc_t level;
+ tdav_codec_h264_common_t* common = (tdav_codec_h264_common_t*)self;
+
+ if (!self || profile != profile_idc_baseline) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ goto bail;
+ }
+
+ if ((ret = tdav_codec_h264_common_init(common))) {
+ TSK_DEBUG_ERROR("tdav_codec_h264_cisco_common_init() faile with error code=%d", ret);
+ goto bail;
+ }
+
+ if ((ret = tdav_codec_h264_common_level_from_size(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, &level))) {
+ TSK_DEBUG_ERROR("Failed to find level for size=[%u, %u]", TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height);
+ goto bail;
+ }
+
+ common->pack_mode_local = H264_PACKETIZATION_MODE;
+ common->profile = profile;
+ common->level = level;
+ // A.2.1.1 Constrained Baseline profile
+ // Conformance of a bitstream to the Constrained Baseline profile is indicated by profile_idc being equal to 66 with
+ // constraint_set1_flag being equal to 1.
+ common->profile_iop = 0xe0; // "constraint_set0_flag=1 and constraint_set1_flag=1" -> Constrained Baseline profile
+ TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS*1000;
+ TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR*1000;
+
+ TMEDIA_CODEC_VIDEO(self)->in.chroma = tmedia_chroma_yuv420p;
+ TMEDIA_CODEC_VIDEO(self)->out.chroma = tmedia_chroma_yuv420p;
+
+ ret = 0;
+
+bail:
+ return ret;
+}
+
+static int tdav_codec_h264_cisco_deinit(tdav_codec_h264_cisco_t* self)
+{
+ if (!self) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tdav_codec_h264_cisco_close((tmedia_codec_t*)self);
+
+ return 0;
+}
+
+#endif /* HAVE_OPENH264 */
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264_cuda.cxx b/tinyDAV/src/codecs/h264/tdav_codec_h264_cuda.cxx
new file mode 100644
index 0000000..f9fbc11
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264_cuda.cxx
@@ -0,0 +1,1130 @@
+/*
+* Copyright (C) 2011 Doubango Telecom <http://www.doubango.org>.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango(DOT)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_h264_cuda.c
+ * @brief H.264 codec plugin using NVIDIA CUDA for encoding/decoding
+ * Env: gpucomputingsdk_4.0.17_win_32, cudatoolkit_4.0.17_win_32 and 280.26-notebook-win7-winvista-32bit-international-whql.
+ * http://developer.download.nvidia.com/compute/DevZone/docs/html/C/doc/CUDA_VideoDecoder_Library.pdf
+ * http://developer.download.nvidia.com/compute/DevZone/docs/html/C/doc/CUDA_VideoEncoder_Library.pdf
+ * 2.0: https://developer.nvidia.com/sites/default/files/akamai/cuda/files/CUDADownloads/NVENC_VideoEncoder_API_ProgGuide.pdf
+ *
+ * RTP payloader/depayloader follows RFC 3984.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango(DOT)org>
+ *
+ */
+#include "tinydav/codecs/h264/tdav_codec_h264_cuda.h"
+
+#if HAVE_CUDA
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "nvcuvenc.lib")
+# pragma comment(lib, "nvcuvid.lib")
+# pragma comment(lib, "cuda.lib")
+
+# pragma comment(lib, "d3d9.lib")
+# pragma comment(lib, "d3dx9.lib")
+#endif
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <cutil_inline.h>
+#include <cuda.h>
+
+#define tdav_codec_h264_cuda_fmtp_set tsk_null
+
+#if !defined(CUDA_MAX_FRM_CNT)
+# define CUDA_MAX_FRM_CNT 10
+#endif
+
+#include "tsk_semaphore.h"
+tsk_semaphore_handle_t *sem = tsk_null;
+
+#define InitH264DecoderInfo(_self) \
+ memset(&_self->decoder.info, 0, sizeof(_self->decoder.info)); \
+ _self->decoder.info.ulCreationFlags = cudaVideoCreate_PreferCUDA; \
+ _self->decoder.info.CodecType = cudaVideoCodec_H264; \
+ _self->decoder.info.ulWidth = TMEDIA_CODEC_VIDEO(_self)->in.width; \
+ _self->decoder.info.ulTargetWidth = TMEDIA_CODEC_VIDEO(_self)->in.width; \
+ _self->decoder.info.ulHeight = TMEDIA_CODEC_VIDEO(_self)->in.height; \
+ _self->decoder.info.ulTargetHeight = TMEDIA_CODEC_VIDEO(_self)->in.height; \
+ _self->decoder.info.ulNumDecodeSurfaces = CUDA_MAX_FRM_CNT; \
+ _self->decoder.info.ulNumOutputSurfaces = 1; \
+ _self->decoder.info.ChromaFormat = cudaVideoChromaFormat_420; \
+ _self->decoder.info.OutputFormat = cudaVideoSurfaceFormat_NV12; \
+ _self->decoder.info.DeinterlaceMode = cudaVideoDeinterlaceMode_Adaptive;
+
+static int CUDAAPI _NVCallback_HandleVideoSequence(void *pvUserData, CUVIDEOFORMAT *pFormat);
+static int CUDAAPI _NVCallback_HandlePictureDecode(void *pvUserData, CUVIDPICPARAMS *pPicParams);
+static int CUDAAPI _NVCallback_HandlePictureDisplay(void *pvUserData, CUVIDPARSERDISPINFO *pPicParams);
+static unsigned char* CUDAAPI _NVCallback_HandleAcquireBitStream(int *pBufferSize, void *pUserdata);
+static void CUDAAPI _NVCallback_HandleReleaseBitStream(int nBytesInBuffer, unsigned char *cb,void *pUserdata);
+static void CUDAAPI _NVCallback_HandleOnBeginFrame(const NVVE_BeginFrameInfo *pbfi, void *pUserdata);
+static void CUDAAPI _NVCallback_HandleOnEndFrame(const NVVE_EndFrameInfo *pefi, void *pUserdata);
+
+static inline void _tdav_codec_h264_cuda_encap(const tdav_codec_h264_cuda_t* h264, const uint8_t* pdata, tsk_size_t size);
+static inline tsk_size_t _tdav_codec_h264_cuda_pict_layout(tdav_codec_h264_cuda_t* self, void**output, tsk_size_t *output_size);
+
+static int tdav_codec_h264_cuda_open(tmedia_codec_t* self)
+{
+ int ret = 0, i;
+ int bestGPU = 0, gpuPerf = 0, adapterCount;
+ static int low_latency = 1;
+ HRESULT hr;
+ CUresult cuResult;
+ D3DPRESENT_PARAMETERS d3dpp;
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ //
+ // encoder
+ //
+ memset(&h264->encoder.clb_params, 0, sizeof(h264->encoder.clb_params));
+ memset(&h264->encoder.ctx_params, 0, sizeof(h264->encoder.ctx_params));
+
+ h264->encoder.ctx_params.iInputSize[0] = TMEDIA_CODEC_VIDEO(h264)->out.width;
+ h264->encoder.ctx_params.iInputSize[1] = TMEDIA_CODEC_VIDEO(h264)->out.height;
+ h264->encoder.ctx_params.iOutputSize[0] = TMEDIA_CODEC_VIDEO(h264)->out.width;
+ h264->encoder.ctx_params.iOutputSize[1] = TMEDIA_CODEC_VIDEO(h264)->out.height;
+ h264->encoder.ctx_params.GPUOffloadLevel= NVVE_GPU_OFFLOAD_DEFAULT;
+ h264->encoder.ctx_params.iSurfaceFormat = (int)IYUV;
+ h264->encoder.ctx_params.iPictureType = (int)FRAME_PICTURE;
+ h264->encoder.ctx_params.Fieldmode = MODE_FRAME;
+ h264->encoder.ctx_params.Presets = (NVVE_PRESETS_TARGET)-1;//Should be iPod, Zune ...
+ h264->encoder.ctx_params.iP_Interval = 1;
+ h264->encoder.ctx_params.iAspectRatio[0] = 4;
+ h264->encoder.ctx_params.iAspectRatio[1] = 3;
+ h264->encoder.ctx_params.iAspectRatio[2] = 0;
+ h264->encoder.ctx_params.iIDR_Period = TMEDIA_CODEC_VIDEO(h264)->out.fps * 3;
+ h264->encoder.ctx_params.iUseDeviceMem = 0;
+ h264->encoder.ctx_params.iDynamicGOP = 0;
+ h264->encoder.ctx_params.RCType = RC_VBR;
+ h264->encoder.ctx_params.iAvgBitrate = 400000;
+ h264->encoder.ctx_params.iPeakBitrate = 800000;
+ h264->encoder.ctx_params.iQP_Level_Intra = 25;
+ h264->encoder.ctx_params.iQP_Level_InterP = 28;
+ h264->encoder.ctx_params.iQP_Level_InterB = 31;
+ h264->encoder.ctx_params.iFrameRate[0] = TMEDIA_CODEC_VIDEO(h264)->out.fps * 1000;
+ h264->encoder.ctx_params.iFrameRate[1] = 1000;
+ h264->encoder.ctx_params.iDeblockMode = 1;
+ h264->encoder.ctx_params.iForceIntra = 0;
+ h264->encoder.ctx_params.iForceIDR = 0;
+ h264->encoder.ctx_params.iClearStat = 0;
+ h264->encoder.ctx_params.DIMode = DI_MEDIAN;
+ h264->encoder.ctx_params.iDisableSPSPPS = 1; // Do not include SPS/PPS frames
+ h264->encoder.ctx_params.iNaluFramingType = 0; // StartCodes
+ h264->encoder.ctx_params.iMultiGPU = 1;
+ switch(TDAV_CODEC_H264_COMMON(h264)->profile){
+ case tdav_codec_h264_bp10:
+ h264->encoder.ctx_params.iDisableCabac = 1;
+ h264->encoder.ctx_params.iProfileLevel = 0xff42;
+ break;
+ case tdav_codec_h264_bp20:
+ h264->encoder.ctx_params.iDisableCabac = 1;
+ h264->encoder.ctx_params.iProfileLevel = 0xff42;
+ break;
+ case tdav_codec_h264_bp30:
+ h264->encoder.ctx_params.iDisableCabac = 1;
+ h264->encoder.ctx_params.iProfileLevel = 0xff42;
+ break;
+ default:
+ break;
+ }
+
+ hr = NVCreateEncoder(&h264->encoder.context);
+ if(FAILED(hr)){
+ TSK_DEBUG_ERROR("NVCreateEncoder failed with error code = %08x", hr);
+ return -2;
+ }
+
+ hr = NVSetCodec(h264->encoder.context, NV_CODEC_TYPE_H264);
+ if(FAILED(hr)){
+ TSK_DEBUG_ERROR("NVSetCodec failed with error code = %08x", hr);
+ return -2;
+ }
+
+ hr = NVSetDefaultParam(h264->encoder.context);
+ if(FAILED(hr)){
+ TSK_DEBUG_ERROR("NVSetDefaultParam() failed with error code = %08x", hr);
+ return -2;
+ }
+
+ hr = NVGetParamValue(h264->encoder.context, NVVE_GET_GPU_COUNT, &h264->encoder.ctx_params.GPU_count);
+ if(SUCCEEDED(hr)){
+ int temp = 0, deviceCount;
+ for (deviceCount=0; deviceCount < h264->encoder.ctx_params.GPU_count; deviceCount++) {
+ NVVE_GPUAttributes GPUAttributes = {0};
+
+ GPUAttributes.iGpuOrdinal = deviceCount;
+ hr = NVGetParamValue(h264->encoder.context, NVVE_GET_GPU_ATTRIBUTES, &GPUAttributes);
+ if(FAILED(hr)){
+ TSK_DEBUG_ERROR("NVGetParamValue(NVVE_GET_GPU_ATTRIBUTES) failed with error code = %08x", hr);
+ continue;
+ }
+
+ temp = GPUAttributes.iClockRate * GPUAttributes.iMultiProcessorCount;
+ temp = temp * _ConvertSMVer2Cores(GPUAttributes.iMajor, GPUAttributes.iMinor);
+
+ if(temp > gpuPerf){
+ gpuPerf = temp;
+ bestGPU = deviceCount;
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("NVGetParamValue(NVVE_GET_GPU_COUNT) failed with error code = %08x", hr);
+ return -2;
+ }
+
+ h264->encoder.ctx_params.iForcedGPU = bestGPU;
+ hr = NVSetParamValue(h264->encoder.context, NVVE_FORCE_GPU_SELECTION, &h264->encoder.ctx_params.iForcedGPU);
+ if(FAILED(hr)){
+ TSK_DEBUG_WARN("NVSetParamValue(NVVE_FORCE_GPU_SELECTION) failed with error code = %08x", hr);
+ }
+
+ hr = NVSetParamValue(h264->encoder.context, NVVE_DEVICE_MEMORY_INPUT, &(h264->encoder.ctx_params.iUseDeviceMem));
+ if(FAILED(hr)){
+ TSK_DEBUG_ERROR("NVSetParamValue(NVVE_OUT_SIZE) failed with error code = %08x", hr);
+ return -2;
+ }
+
+ h264->encoder.buffer_size = (h264->encoder.ctx_params.iOutputSize[1] * h264->encoder.ctx_params.iOutputSize[0] * 3) >> 4;
+ if(!h264->encoder.buffer && !(h264->encoder.buffer = tsk_realloc(h264->encoder.buffer, h264->encoder.buffer_size))){
+ TSK_DEBUG_ERROR("Failed to allocate buffer with size=%u", h264->encoder.buffer_size);
+ h264->encoder.buffer_size = 0;
+ return -2;
+ }
+
+ hr = NVSetParamValue(h264->encoder.context,NVVE_OUT_SIZE, &(h264->encoder.ctx_params.iOutputSize)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_OUT_SIZE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_IN_SIZE, &(h264->encoder.ctx_params.iInputSize)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_IN_SIZE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_MULTI_GPU, &(h264->encoder.ctx_params.iMultiGPU)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_MULTI_GPU) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_ASPECT_RATIO, &(h264->encoder.ctx_params.iAspectRatio));if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_ASPECT_RATIO) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_FIELD_ENC_MODE, &(h264->encoder.ctx_params.Fieldmode)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_FIELD_ENC_MODE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_P_INTERVAL, &(h264->encoder.ctx_params.iP_Interval)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_P_INTERVAL) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_IDR_PERIOD, &(h264->encoder.ctx_params.iIDR_Period)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_IDR_PERIOD) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_DYNAMIC_GOP, &(h264->encoder.ctx_params.iDynamicGOP)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_DYNAMIC_GOP) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_RC_TYPE, &(h264->encoder.ctx_params.RCType)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_RC_TYPE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_AVG_BITRATE, &(h264->encoder.ctx_params.iAvgBitrate)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_AVG_BITRATE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_PEAK_BITRATE, &(h264->encoder.ctx_params.iPeakBitrate)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_PEAK_BITRATE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_QP_LEVEL_INTRA, &(h264->encoder.ctx_params.iQP_Level_Intra)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_OUT_SIZE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_QP_LEVEL_INTER_P,&(h264->encoder.ctx_params.iQP_Level_InterP)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_QP_LEVEL_INTER_P) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_QP_LEVEL_INTER_B,&(h264->encoder.ctx_params.iQP_Level_InterB)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_QP_LEVEL_INTER_B) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_FRAME_RATE, &(h264->encoder.ctx_params.iFrameRate)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_FRAME_RATE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_DEBLOCK_MODE, &(h264->encoder.ctx_params.iDeblockMode)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_DEBLOCK_MODE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_PROFILE_LEVEL, &(h264->encoder.ctx_params.iProfileLevel)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_PROFILE_LEVEL) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_FORCE_INTRA, &(h264->encoder.ctx_params.iForceIntra)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_FORCE_INTRA) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_FORCE_IDR, &(h264->encoder.ctx_params.iForceIDR)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_FORCE_IDR) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_CLEAR_STAT, &(h264->encoder.ctx_params.iClearStat)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_CLEAR_STAT) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_SET_DEINTERLACE,&(h264->encoder.ctx_params.DIMode)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_SET_DEINTERLACE) failed with error code = %08x", hr); }
+ if (h264->encoder.ctx_params.Presets != -1) {
+ hr = NVSetParamValue(h264->encoder.context,NVVE_PRESETS, &(h264->encoder.ctx_params.Presets)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_PRESETS) failed with error code = %08x", hr); }
+ }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_DISABLE_CABAC, &(h264->encoder.ctx_params.iDisableCabac)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_DISABLE_CABAC) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_CONFIGURE_NALU_FRAMING_TYPE, &(h264->encoder.ctx_params.iNaluFramingType)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_CONFIGURE_NALU_FRAMING_TYPE) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_DISABLE_SPS_PPS,&(h264->encoder.ctx_params.iDisableSPSPPS)); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_DISABLE_SPS_PPS) failed with error code = %08x", hr); }
+ hr = NVSetParamValue(h264->encoder.context,NVVE_LOW_LATENCY,&low_latency); if (hr!=S_OK) { TSK_DEBUG_WARN("NVSetParamValue(NVVE_LOW_LATENCY) failed with error code = %08x", hr); }
+
+ h264->encoder.clb_params.pfnacquirebitstream = _NVCallback_HandleAcquireBitStream;
+ h264->encoder.clb_params.pfnonbeginframe = _NVCallback_HandleOnBeginFrame;
+ h264->encoder.clb_params.pfnonendframe = _NVCallback_HandleOnEndFrame;
+ h264->encoder.clb_params.pfnreleasebitstream = _NVCallback_HandleReleaseBitStream;
+ NVRegisterCB(h264->encoder.context, h264->encoder.clb_params, h264);
+
+
+ hr = NVCreateHWEncoder(h264->encoder.context);
+ if(FAILED(hr)){
+ TSK_DEBUG_ERROR("NVCreateHWEncoder failed with error code = %08x", hr);
+ return -2;
+ }
+
+
+
+ //
+ // decoder
+ //
+ if((cuResult = cuInit(0)) != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuInit(0) failed with error code = %d", (int)cuResult);
+ return -3;
+ }
+
+ InitH264DecoderInfo(h264);
+
+ h264->decoder.cu_device = cutilDrvGetMaxGflopsGraphicsDeviceId();
+
+#if _DEBUG || DEBUG
+ {
+ int major, minor;
+ size_t totalGlobalMem;
+ char deviceName[256];
+ cuDeviceComputeCapability(&major, &minor, h264->decoder.cu_device);
+ cuDeviceGetName(deviceName, sizeof(deviceName), h264->decoder.cu_device);
+ TSK_DEBUG_INFO("Using GPU Device %d: %s has SM %d.%d compute capability", h264->decoder.cu_device, deviceName, major, minor);
+
+ cutilDrvSafeCallNoSync(cuDeviceTotalMem(&totalGlobalMem, h264->decoder.cu_device) );
+ TSK_DEBUG_INFO("Total amount of global memory in GPU device: %4.4f MB", (float)totalGlobalMem/(1024*1024));
+ }
+#endif
+
+ // create Direct3D instance
+ h264->decoder.dx_d3d = Direct3DCreate9(D3D_SDK_VERSION);
+ if(!h264->decoder.dx_d3d){
+ TSK_DEBUG_ERROR("Direct3DCreate9 failed");
+ return -3;
+ }
+ adapterCount = h264->decoder.dx_d3d->GetAdapterCount();
+ for(i=0; i<adapterCount; ++i){
+ ZeroMemory(&d3dpp, sizeof(d3dpp));
+ d3dpp.Windowed = TRUE;
+ d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
+ d3dpp.BackBufferWidth = h264->decoder.info.ulTargetWidth;
+ d3dpp.BackBufferHeight = h264->decoder.info.ulTargetHeight;
+ d3dpp.BackBufferCount = 1;
+ d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
+ d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ d3dpp.Flags = D3DPRESENTFLAG_VIDEO;//D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
+ hr = h264->decoder.dx_d3d->CreateDevice(i,
+ D3DDEVTYPE_HAL,
+ GetDesktopWindow(),
+ D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING,
+ &d3dpp,
+ &h264->decoder.dx_d3ddevice);
+ if(hr == S_OK){
+ cuResult = cuD3D9CtxCreate(&h264->decoder.cu_context, &h264->decoder.cu_device, 0, h264->decoder.dx_d3ddevice);
+ if(cuResult == CUDA_SUCCESS){
+ break;
+ }
+ if(h264->decoder.dx_d3ddevice){
+ h264->decoder.dx_d3ddevice->Release();
+ h264->decoder.dx_d3ddevice = NULL;
+ }
+ }
+ }
+
+ memset(&h264->decoder.cu_paser_params, 0, sizeof(h264->decoder.cu_paser_params));
+ h264->decoder.cu_paser_params.CodecType = cudaVideoCodec_H264;
+ h264->decoder.cu_paser_params.ulMaxNumDecodeSurfaces = CUDA_MAX_FRM_CNT;
+ h264->decoder.cu_paser_params.pUserData = h264;
+ h264->decoder.cu_paser_params.pfnSequenceCallback = _NVCallback_HandleVideoSequence;
+ h264->decoder.cu_paser_params.pfnDecodePicture = _NVCallback_HandlePictureDecode;
+ h264->decoder.cu_paser_params.pfnDisplayPicture = _NVCallback_HandlePictureDisplay;
+ cuResult = cuvidCreateVideoParser(&h264->decoder.cu_parser, &h264->decoder.cu_paser_params);
+ if(cuResult != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuvidCreateVideoParser(0) failed with error code = %d", (int)cuResult);
+ return -3;
+ }
+
+ cuResult = cuvidCreateDecoder(&h264->decoder.context, &h264->decoder.info);
+ if(CUDA_SUCCESS != cuResult){
+ TSK_DEBUG_ERROR("cuvidCreateDecoder failed with error code=%d", (int)cuResult);
+ return -3;
+ }
+
+ return ret;
+}
+
+static int tdav_codec_h264_cuda_close(tmedia_codec_t* self)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(h264->encoder.context){
+ NVDestroyEncoder(h264->encoder.context);
+ h264->encoder.context = NULL;
+ }
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_cuda_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ NVVE_EncodeFrameParams efparams;
+ int ret = 0;
+ unsigned long flags = 0;
+ HRESULT hr;
+
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Codec not opened");
+ return 0;
+ }
+
+ if((h264->encoder.ctx_params.iOutputSize[1] * h264->encoder.ctx_params.iOutputSize[0] * 3)>>1 != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+
+ //return 0;
+
+ efparams.Width = h264->encoder.ctx_params.iOutputSize[0];
+ efparams.Height = h264->encoder.ctx_params.iOutputSize[1];
+ efparams.Pitch = (h264->encoder.ctx_params.nDeviceMemPitch ? h264->encoder.ctx_params.nDeviceMemPitch : h264->encoder.ctx_params.iOutputSize[0]);
+ efparams.PictureStruc = (NVVE_PicStruct)h264->encoder.ctx_params.iPictureType;
+ efparams.SurfFmt = (NVVE_SurfaceFormat)h264->encoder.ctx_params.iSurfaceFormat;
+ efparams.progressiveFrame = (h264->encoder.ctx_params.iSurfaceFormat == 3) ? 1 : 0;
+ efparams.repeatFirstField = 0;
+ efparams.topfieldfirst = (h264->encoder.ctx_params.iSurfaceFormat == 1) ? 1 : 0;
+ efparams.picBuf = (unsigned char *)in_data;
+ efparams.bLast = 0;
+
+ // send keyframe for:
+ // - the first frame
+ // - every second within the first 4seconds
+ // - every 7 seconds after the first 4seconds
+ if(h264->encoder.frame_count++ == 0
+ ||
+ ( (h264->encoder.frame_count < (int)TMEDIA_CODEC_VIDEO(h264)->out.fps * 4) && ((h264->encoder.frame_count % TMEDIA_CODEC_VIDEO(h264)->out.fps)==0) )
+ ||
+ ( (h264->encoder.frame_count % (TMEDIA_CODEC_VIDEO(h264)->out.fps * 5))==0 )
+ )
+ {
+ flags |= 0x04; // FORCE IDR
+ if(h264->encoder.ctx_params.iDisableSPSPPS){
+ unsigned char SPSPPSBuff[1024];
+ int SPSPPSBuffSize = sizeof(SPSPPSBuff);
+ hr = NVGetSPSPPS(h264->encoder.context, SPSPPSBuff, SPSPPSBuffSize, &SPSPPSBuffSize);
+ if(SUCCEEDED(hr)){
+ int size = 0;
+ while(size < SPSPPSBuffSize - 2){
+ int16_t next_size = ((int16_t)SPSPPSBuff[size])<<1 | ((int16_t)SPSPPSBuff[size + 1]);
+ _tdav_codec_h264_cuda_encap(h264, &SPSPPSBuff[size + 2], next_size);
+ size+=next_size + 2;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("NVGetSPSPPS failed with error code = %08x", hr)
+ }
+ }
+ }
+
+ hr = NVEncodeFrame(h264->encoder.context, &efparams, flags, NULL);
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_cuda_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_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = (const trtp_rtp_header_t*)proto_hdr;
+ const uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size = 0, retsize = 0, size_to_copy = 0;
+ int ret = 0;
+ tsk_bool_t append_scp = tsk_false;
+ static tsk_size_t xmax_size = (1920 * 1080 * 3) >> 3;
+ static tsk_size_t start_code_prefix_size = sizeof(H264_START_CODE_PREFIX);
+
+ // Packet lost?
+ if(h264->decoder.last_seq != (rtp_hdr->seq_num - 1) && h264->decoder.last_seq){
+ if(h264->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ h264->decoder.last_seq = rtp_hdr->seq_num;
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ if(*((uint8_t*)in_data) >> 7){
+ TSK_DEBUG_WARN("F=1");
+ /* reset accumulator */
+ h264->decoder.accumulator_pos = 0;
+ goto bail;
+ }
+
+ // get payload
+ if((ret = tdav_codec_h264_get_pay(in_data, in_size, (const void**)&pay_ptr, &pay_size, &append_scp)) || !pay_ptr || !pay_size){
+ TSK_DEBUG_ERROR("Depayloader failed to get H.264 content");
+ goto bail;
+ }
+ //append_scp = tsk_true;
+ size_to_copy = pay_size + (append_scp ? start_code_prefix_size : 0);
+
+ // start-accumulator
+ if(!h264->decoder.accumulator){
+ if(size_to_copy > xmax_size){
+ TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", size_to_copy, xmax_size);
+ goto bail;
+ }
+ if(!(h264->decoder.accumulator = tsk_calloc(size_to_copy, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocated new buffer");
+ goto bail;
+ }
+ h264->decoder.accumulator_size = size_to_copy;
+ }
+ if((h264->decoder.accumulator_pos + size_to_copy) >= xmax_size){
+ TSK_DEBUG_ERROR("BufferOverflow");
+ h264->decoder.accumulator_pos = 0;
+ goto bail;
+ }
+ if((h264->decoder.accumulator_pos + size_to_copy) > h264->decoder.accumulator_size){
+ if(!(h264->decoder.accumulator = tsk_realloc(h264->decoder.accumulator, (h264->decoder.accumulator_pos + size_to_copy)))){
+ TSK_DEBUG_ERROR("Failed to reallocated new buffer");
+ h264->decoder.accumulator_pos = 0;
+ h264->decoder.accumulator_size = 0;
+ goto bail;
+ }
+ h264->decoder.accumulator_size = (h264->decoder.accumulator_pos + size_to_copy);
+ }
+
+ if(append_scp){
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], H264_START_CODE_PREFIX, start_code_prefix_size);
+ h264->decoder.accumulator_pos += start_code_prefix_size;
+ }
+ memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], pay_ptr, pay_size);
+ h264->decoder.accumulator_pos += pay_size;
+ // end-accumulator
+
+ if(rtp_hdr->marker){
+ CUVIDSOURCEDATAPACKET pkt;
+ CUresult cuResult;
+ pkt.flags = 0;
+ pkt.payload_size = (unsigned long) h264->decoder.accumulator_pos;
+ pkt.payload = (unsigned char *)h264->decoder.accumulator;
+ pkt.timestamp = 0;
+
+ // reset accumulator
+ h264->decoder.accumulator_pos = 0;
+ cuResult = cuvidParseVideoData(h264->decoder.cu_parser, &pkt);
+ if(cuResult != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuvidParseVideoData() failed with error code = %d", (int)cuResult);
+ goto bail;
+ }
+
+ if(h264->decoder.cu_buffer_avail){
+ h264->decoder.cu_buffer_avail = tsk_false;
+ if((retsize = _tdav_codec_h264_cuda_pict_layout(h264, out_data, out_max_size)) == 0){
+ TSK_DEBUG_ERROR("_tdav_codec_h264_cuda_pict_layout failed");
+ goto bail;
+ }
+ }
+ }
+
+bail:
+ return retsize;
+}
+
+static tsk_bool_t tdav_codec_h264_cuda_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)codec;
+ profile_idc_t profile;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ /* Check whether the profile match (If the profile is missing, then we consider that it's ok) */
+ if(((profile = tdav_codec_h264_common_get_profile(fmtp)) != tdav_codec_h264_bp99) && (profile != TDAV_CODEC_H264_COMMON(h264)->profile)){
+ TSK_DEBUG_INFO("Profile not matching");
+ return tsk_false;
+ }
+
+ TMEDIA_CODEC_VIDEO(h264)->in.width = 800, TMEDIA_CODEC_VIDEO(h264)->in.height = 640;
+ TMEDIA_CODEC_VIDEO(h264)->out.width = 800, TMEDIA_CODEC_VIDEO(h264)->out.height = 640;
+ //TMEDIA_CODEC_VIDEO(h264)->in.width = 352, TMEDIA_CODEC_VIDEO(h264)->in.height = 288;
+ //TMEDIA_CODEC_VIDEO(h264)->out.width = 352, TMEDIA_CODEC_VIDEO(h264)->out.height = 288;
+
+ return tsk_true;
+}
+
+static char* tdav_codec_h264_cuda_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ char* fmtp = tsk_null;
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)self;
+
+ switch(TDAV_CODEC_H264_COMMON(h264)->profile){
+ case tdav_codec_h264_bp10:
+ fmtp = tsk_strdup("profile-level-id=42e00a");
+ break;
+ case tdav_codec_h264_bp20:
+ fmtp = tsk_strdup("profile-level-id=42e014");
+ break;
+ case tdav_codec_h264_bp30:
+ fmtp = tsk_strdup("profile-level-id=42e01e");
+ break;
+ }
+
+ //1080p(1920 x 1080), 720p(1280 x 720), SVGA(800 x 600), VGA(640 x 480), 4CIF(704 x 576), CIF(352 x 288), QCIF(176 x 144), SQCIF(128 x 96)
+ return fmtp;
+}
+
+tsk_bool_t tdav_codec_h264_cuda_is_supported()
+{
+ static tsk_bool_t __already_checked = tsk_false;
+ static tsk_bool_t __is_supported = tsk_false;
+ if(!__already_checked){
+ HRESULT hr;
+ __already_checked = tsk_true;
+ hr = NVGetHWEncodeCaps();
+ if(SUCCEEDED(hr)){
+ NVEncoder encoder;
+ hr = NVCreateEncoder(&encoder);
+ if(SUCCEEDED(hr)){
+ hr = NVIsSupportedCodec(encoder, NV_CODEC_TYPE_H264);
+ __is_supported = SUCCEEDED(hr);
+ }
+ else{
+ TSK_DEBUG_ERROR("NVCreateEncoder() failed with error code = %08x", hr);
+ }
+ if(encoder){
+ NVDestroyEncoder(encoder);
+ encoder = NULL;
+ }
+ }
+ }
+ return __is_supported;
+}
+
+static int tdav_codec_h264_cuda_init(tdav_codec_h264_cuda_t* self, profile_idc_t profile)
+{
+ int ret = 0;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if((ret = tdav_codec_h264_common_init(TDAV_CODEC_H264_COMMON(self)))){
+ TSK_DEBUG_ERROR("tdav_codec_h264_common_init() faile with error code=%d", ret);
+ return ret;
+ }
+
+ if(!self->decoder.mutex && !(self->decoder.mutex = tsk_mutex_create_2(tsk_false))){
+ TSK_DEBUG_ERROR("Failed to create mutex");
+ return -2;
+ }
+
+ sem = tsk_semaphore_create_2(1);
+
+ TDAV_CODEC_H264_COMMON(self)->pack_mode_local = H264_PACKETIZATION_MODE;
+ TDAV_CODEC_H264_COMMON(self)->profile = profile;
+ TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS*1000;
+ TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR*1000;
+
+ // At this time self->plugin is Null
+ TMEDIA_CODEC_VIDEO(self)->in.width = TMEDIA_CODEC_VIDEO(self)->out.width = 176;
+ TMEDIA_CODEC_VIDEO(self)->in.height = TMEDIA_CODEC_VIDEO(self)->out.height = 144;
+ TMEDIA_CODEC_VIDEO(self)->in.fps = TMEDIA_CODEC_VIDEO(self)->out.fps = 15;
+ TMEDIA_CODEC_VIDEO(self)->in.chroma = tmedia_chroma_yuv420p;// no choice
+
+ return 0;
+}
+
+static int tdav_codec_h264_cuda_deinit(tdav_codec_h264_cuda_t* self)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)self;
+
+ if(!h264){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(h264->encoder.context){
+ NVDestroyEncoder(h264->encoder.context);
+ h264->encoder.context = NULL;
+ }
+ TSK_FREE(h264->encoder.buffer);
+ h264->encoder.buffer_size = 0;
+
+ if(h264->decoder.context){
+ cuvidDestroyDecoder(h264->decoder.context);
+ h264->decoder.context = NULL;
+ }
+ if(h264->decoder.cu_context){
+ cuCtxDestroy(h264->decoder.cu_context);
+ h264->decoder.cu_context = NULL;
+ }
+ if (h264->decoder.dx_d3ddevice){
+ h264->decoder.dx_d3ddevice->Release();
+ h264->decoder.dx_d3ddevice = NULL;
+ }
+ if (h264->decoder.dx_d3d){
+ h264->decoder.dx_d3d->Release();
+ h264->decoder.dx_d3d = NULL;
+ }
+ if(h264->decoder.cu_parser){
+ cuvidDestroyVideoParser(h264->decoder.cu_parser);
+ h264->decoder.cu_parser = NULL;
+ }
+ if(h264->decoder.cu_buffer){
+ cuMemFreeHost(h264->decoder.cu_buffer);
+ h264->decoder.cu_buffer = NULL;
+ }
+ h264->decoder.cu_buffer_size = 0;
+ if(self->decoder.mutex){
+ tsk_mutex_destroy(&self->decoder.mutex);
+ }
+
+ TSK_FREE(h264->decoder.accumulator);
+ h264->decoder.accumulator_pos = 0;
+ h264->decoder.accumulator_size = 0;
+
+ return 0;
+}
+
+static inline void _tdav_codec_h264_cuda_encap(const tdav_codec_h264_cuda_t* h264, const uint8_t* pdata, tsk_size_t size)
+{
+ register int32_t i;
+ int32_t last_scp, prev_scp;
+ static int32_t size_of_scp = sizeof(H264_START_CODE_PREFIX); /* we know it's equal to 4 ..but */
+
+ if(!pdata || !size){
+ return;
+ }
+
+ last_scp = 0, prev_scp = 0;
+
+ for(i = size_of_scp; i<(int32_t)(size - size_of_scp); i++){
+ if(pdata[i] == H264_START_CODE_PREFIX[0] && pdata[i+1] == H264_START_CODE_PREFIX[1] && pdata[i+2] == H264_START_CODE_PREFIX[2] && pdata[i+3] == H264_START_CODE_PREFIX[3]){ /* Found Start Code Prefix */
+ prev_scp = last_scp;
+ if((i - last_scp) >= H264_RTP_PAYLOAD_SIZE || 1){
+ tdav_codec_h264_rtp_callback(TDAV_CODEC_H264_COMMON(h264), pdata + prev_scp,
+ (i - prev_scp), (prev_scp == size));
+ }
+ last_scp = i;
+ }
+ }
+ if(last_scp < (int32_t)size){
+ tdav_codec_h264_rtp_callback(TDAV_CODEC_H264_COMMON(h264), pdata + last_scp,
+ (size - last_scp), tsk_true);
+ }
+}
+
+static inline tsk_size_t _tdav_codec_h264_cuda_pict_layout(tdav_codec_h264_cuda_t* self, void**output, tsk_size_t *output_size)
+{
+ if(self && self->decoder.cu_buffer && self->decoder.cu_buffer_size){
+ unsigned int w = TMEDIA_CODEC_VIDEO(self)->in.width;
+ unsigned int h = TMEDIA_CODEC_VIDEO(self)->in.height;
+ unsigned int pitch = self->decoder.cu_buffer_pitch;
+ tsk_size_t xsize = (w * h * 3) >> 1;
+ // resize if too short
+ if(*output_size<xsize){
+ if((*output = tsk_realloc(*output, xsize))){
+ *output_size = xsize;
+ }
+ else{
+ *output_size = 0;
+ return 0;
+ }
+ }
+
+ tsk_semaphore_decrement(sem);
+
+ register unsigned int y;
+ register unsigned int x, x2;
+ const unsigned char *p = (const unsigned char *)self->decoder.cu_buffer;
+ register unsigned char *iyuv = (unsigned char *)*output, *i = iyuv, *j;
+ // copy luma
+ for (y=0; y<h; y++){
+ memcpy(i+y*w, p+y*pitch, w);
+ }
+ // de-interleave chroma (NV12 stored as U,V,U,V,...)
+ p += h * pitch;
+ i += h * w;
+ j = i + (h/2)*(w/2);
+ for (y=0; y<h/2; y++){
+ for (x=0, x2=0; x<w/2; x++, x2+=2) i[x] = p[x2], j[x] = p[x2+1];
+ p += pitch, i += w/2, j += w/2;
+ }
+
+ tsk_semaphore_increment(sem);
+
+ return xsize;
+ }
+ return 0;
+}
+
+static int CUDAAPI _NVCallback_HandleVideoSequence(void *pvUserData, CUVIDEOFORMAT *pFormat)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)pvUserData;
+ if(!h264 || !pFormat){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;//error
+ }
+
+ int ret = 1;
+
+ if(pFormat->coded_width != TMEDIA_CODEC_VIDEO(h264)->in.width || pFormat->coded_height != TMEDIA_CODEC_VIDEO(h264)->in.height){
+ tsk_mutex_lock(h264->decoder.mutex);
+
+ TMEDIA_CODEC_VIDEO(h264)->in.width = pFormat->coded_width;
+ TMEDIA_CODEC_VIDEO(h264)->in.height = pFormat->coded_height;
+
+ InitH264DecoderInfo(h264);
+ CUresult cuResult;
+ if(h264->decoder.context){
+ cuResult = cuvidDestroyDecoder(h264->decoder.context);
+ if(CUDA_SUCCESS != cuResult){
+ TSK_DEBUG_ERROR("cuvidDestroyDecoder failed with error code=%d", (int)cuResult);
+ ret = 0;
+ }
+ h264->decoder.context = NULL;
+ }
+ cuResult = cuvidCreateDecoder(&h264->decoder.context, &h264->decoder.info);
+ if(CUDA_SUCCESS != cuResult){
+ TSK_DEBUG_ERROR("cuvidCreateDecoder failed with error code=%d", (int)cuResult);
+ ret = 0;
+ }
+
+ tsk_mutex_unlock(h264->decoder.mutex);
+ }
+
+
+
+ return ret;//success
+}
+
+static int CUDAAPI _NVCallback_HandlePictureDecode(void *pvUserData, CUVIDPICPARAMS *pPicParams)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)pvUserData;
+ if(!h264 || !pPicParams){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;//error
+ }
+
+ tsk_mutex_lock(h264->decoder.mutex);
+ CUresult cuResult = cuvidDecodePicture(h264->decoder.context, pPicParams);
+ tsk_mutex_unlock(h264->decoder.mutex);
+
+ if(cuResult != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuvidDecodePicture failed with error code= %d", cuResult);
+ return 0;//error
+ }
+
+ return 1;//success
+}
+
+static int CUDAAPI _NVCallback_HandlePictureDisplay(void *pvUserData, CUVIDPARSERDISPINFO *pPicParams)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)pvUserData;
+ CUVIDPROCPARAMS vpp;
+ CUdeviceptr devPtr;
+ CUresult cuResult;
+ tsk_size_t nv12_size;
+ tsk_bool_t mapped = tsk_false;
+ int ret = 1;//success
+
+ if(!h264 || !pPicParams){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;//error
+ }
+tsk_semaphore_decrement(sem);
+ cuResult = cuCtxPushCurrent(h264->decoder.cu_context);
+ if(cuResult != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuCtxPushCurrent failed with error code = %d", (int)cuResult);
+ ret = 0;//error
+ goto bail;
+ }
+
+ memset(&vpp, 0, sizeof(vpp));
+ vpp.progressive_frame = pPicParams->progressive_frame;
+ vpp.top_field_first = pPicParams->top_field_first;
+ cuResult = cuvidMapVideoFrame(h264->decoder.context, pPicParams->picture_index, &devPtr, &h264->decoder.cu_buffer_pitch, &vpp);
+
+ if(cuResult != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuvidMapVideoFrame failed with error code = %d", (int)cuResult);
+ ret = 0;//error
+ goto bail;
+ }
+ mapped = tsk_true;
+ nv12_size = h264->decoder.cu_buffer_pitch * (h264->decoder.info.ulTargetHeight + h264->decoder.info.ulTargetHeight/2); // 12bpp
+ //nv12_size = (w * h * 3) >> 1;
+ if ((!h264->decoder.cu_buffer) || (nv12_size > h264->decoder.cu_buffer_size)){
+ h264->decoder.cu_buffer_size = 0;
+ if (h264->decoder.cu_buffer){
+ cuResult = cuMemFreeHost(h264->decoder.cu_buffer);
+ h264->decoder.cu_buffer = NULL;
+ }
+ cuResult = cuMemAllocHost((void**)&h264->decoder.cu_buffer, nv12_size);
+ if (cuResult != CUDA_SUCCESS){
+ TSK_DEBUG_ERROR("cuMemAllocHost failed to allocate %d bytes (error code=%d)", nv12_size, (int)cuResult);
+ h264->decoder.cu_buffer = 0;
+ h264->decoder.cu_buffer_size = 0;
+ ret = 0;//error
+ }
+ else{
+ h264->decoder.cu_buffer_size = nv12_size;
+ }
+ }
+ if(h264->decoder.cu_buffer){
+ cuResult = cuMemcpyDtoH(h264->decoder.cu_buffer, devPtr, nv12_size);
+ }
+
+bail:
+ if(mapped){
+ cuResult = cuvidUnmapVideoFrame(h264->decoder.context, devPtr);
+ }
+ cuResult = cuCtxPopCurrent(NULL);
+tsk_semaphore_increment(sem);
+ h264->decoder.cu_buffer_avail = (ret == 1);
+ return ret;
+}
+
+static unsigned char* CUDAAPI _NVCallback_HandleAcquireBitStream(int *pBufferSize, void *pUserdata)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)pUserdata;
+ if(!h264 || !pBufferSize){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ *pBufferSize = (int)h264->encoder.buffer_size;
+ return (unsigned char*)h264->encoder.buffer;
+}
+
+static void CUDAAPI _NVCallback_HandleReleaseBitStream(int nBytesInBuffer, unsigned char *cb,void *pUserdata)
+{
+ tdav_codec_h264_cuda_t* h264 = (tdav_codec_h264_cuda_t*)pUserdata;
+ if(!h264 || !cb || !nBytesInBuffer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return;
+ }
+ _tdav_codec_h264_cuda_encap(h264, cb, (tsk_size_t)nBytesInBuffer);
+
+ return;
+}
+
+static void CUDAAPI _NVCallback_HandleOnBeginFrame(const NVVE_BeginFrameInfo *pbfi, void *pUserdata)
+{
+ return;
+}
+
+static void CUDAAPI _NVCallback_HandleOnEndFrame(const NVVE_EndFrameInfo *pefi, void *pUserdata)
+{
+ return;
+}
+
+/* ============ H.264 Base Profile 1.0 Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_cuda_bp10_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_cuda_t *h264 = (tdav_codec_h264_cuda_t *)self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h264_cuda_init(h264, tdav_codec_h264_bp10);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_cuda_bp10_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_cuda_t *h264 = (tdav_codec_h264_cuda_t *)self;
+ if(h264){
+ /* deinit base */
+ tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
+ /* deinit self */
+ tdav_codec_h264_cuda_deinit(h264);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_cuda_bp10_def_s =
+{
+ sizeof(tdav_codec_h264_cuda_t),
+ tdav_codec_h264_cuda_bp10_ctor,
+ tdav_codec_h264_cuda_bp10_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_cuda_bp10_plugin_def_s =
+{
+ &tdav_codec_h264_cuda_bp10_def_s,
+
+ tmedia_video,
+ "H264",
+ "H264 Base Profile 1.0 using CUDA",
+ TMEDIA_CODEC_FORMAT_H264_BP10,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_h264_cuda_open,
+ tdav_codec_h264_cuda_close,
+ tdav_codec_h264_cuda_encode,
+ tdav_codec_h264_cuda_decode,
+ tdav_codec_h264_cuda_sdp_att_match,
+ tdav_codec_h264_cuda_sdp_att_get,
+ tdav_codec_h264_cuda_fmtp_set
+};
+extern const tmedia_codec_plugin_def_t *tdav_codec_h264_cuda_bp10_plugin_def_t = &tdav_codec_h264_cuda_bp10_plugin_def_s;
+
+
+/* ============ H.264 Base Profile 2.0 Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_cuda_bp20_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_cuda_t *h264 = (tdav_codec_h264_cuda_t *)self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h264_cuda_init(h264, tdav_codec_h264_bp20);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_cuda_bp20_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_cuda_t *h264 = (tdav_codec_h264_cuda_t *)self;
+ if(h264){
+ /* deinit base */
+ tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
+ /* deinit self */
+ tdav_codec_h264_cuda_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_cuda_bp20_def_s =
+{
+ sizeof(tdav_codec_h264_cuda_t),
+ tdav_codec_h264_cuda_bp20_ctor,
+ tdav_codec_h264_cuda_bp20_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_cuda_bp20_plugin_def_s =
+{
+ &tdav_codec_h264_cuda_bp20_def_s,
+
+ tmedia_video,
+ "H264",
+ "H264 Base Profile 2.0 using CUDA",
+ TMEDIA_CODEC_FORMAT_H264_BP20,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {352, 288, 15},
+
+ tdav_codec_h264_cuda_open,
+ tdav_codec_h264_cuda_close,
+ tdav_codec_h264_cuda_encode,
+ tdav_codec_h264_cuda_decode,
+ tdav_codec_h264_cuda_sdp_att_match,
+ tdav_codec_h264_cuda_sdp_att_get,
+ tdav_codec_h264_cuda_fmtp_set
+};
+extern const tmedia_codec_plugin_def_t *tdav_codec_h264_cuda_bp20_plugin_def_t = &tdav_codec_h264_cuda_bp20_plugin_def_s;
+
+
+/* ============ H.264 Base Profile 3.0 Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_cuda_bp30_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_cuda_t *h264 = (tdav_codec_h264_cuda_t *)self;
+ if(h264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ tdav_codec_h264_cuda_init(h264, tdav_codec_h264_bp30);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_cuda_bp30_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_cuda_t *h264 = (tdav_codec_h264_cuda_t *)self;
+ if(h264){
+ /* deinit base */
+ tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
+ /* deinit self */
+ tdav_codec_h264_cuda_deinit(h264);
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_cuda_bp30_def_s =
+{
+ sizeof(tdav_codec_h264_cuda_t),
+ tdav_codec_h264_cuda_bp30_ctor,
+ tdav_codec_h264_cuda_bp30_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_cuda_bp30_plugin_def_s =
+{
+ &tdav_codec_h264_cuda_bp30_def_s,
+
+ tmedia_video,
+ "H264",
+ "H264 Base Profile 3.0 using CUDA",
+ TMEDIA_CODEC_FORMAT_H264_BP30,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {352, 288, 15},
+
+ tdav_codec_h264_cuda_open,
+ tdav_codec_h264_cuda_close,
+ tdav_codec_h264_cuda_encode,
+ tdav_codec_h264_cuda_decode,
+ tdav_codec_h264_cuda_sdp_att_match,
+ tdav_codec_h264_cuda_sdp_att_get,
+ tdav_codec_h264_cuda_fmtp_set
+};
+extern const tmedia_codec_plugin_def_t *tdav_codec_h264_cuda_bp30_plugin_def_t = &tdav_codec_h264_cuda_bp30_plugin_def_s;
+
+
+#endif /* HAVE_CUDA */
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx b/tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx
new file mode 100644
index 0000000..49f9e1c
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx
@@ -0,0 +1,2221 @@
+/*
+* Copyright (C) 2014-2015 Mamadou DIOP.
+*
+* 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_h264_intel.cxx
+* @brief H.264 codec plugin using Intel Media SDK 2014 R2 for Clients (https://software.intel.com/en-us/media-solutions-portal) v1.1 for encoding/decoding.
+* Low latency encoding/decoding: https://software.intel.com/en-us/articles/video-conferencing-features-of-intel-media-software-development-kit
+*/
+#include "tinydav/codecs/h264/tdav_codec_h264_intel.h"
+
+#if HAVE_INTEL_MEDIA_SDK
+
+#include "tinydav/codecs/h264/tdav_codec_h264_common.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tinymedia/tmedia_codec.h"
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_thread.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <mfxvideo++.h>
+
+#if defined(_MSC_VER)
+# pragma comment(lib, "libmfx.lib")
+#endif /* _MSC_VER */
+
+#if !defined(INTEL_DX11_D3D)
+# define INTEL_DX11_D3D 1
+#endif /* INTEL_DX11_D3D */
+
+#if INTEL_DX11_D3D
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#include <atlbase.h>
+# if defined(_MSC_VER)
+# pragma comment(lib, "d3d11.lib")
+# pragma comment(lib, "dxgi.lib")
+# endif /* _MSC_VER */
+
+#endif /* INTEL_DX11_D3D */
+
+#define INTEL_DEBUG_INFO(FMT, ...) TSK_DEBUG_INFO("[MSDK H264 Codec] " FMT, ##__VA_ARGS__)
+#define INTEL_DEBUG_WARN(FMT, ...) TSK_DEBUG_WARN("[MSDK H264 Codec] " FMT, ##__VA_ARGS__)
+#define INTEL_DEBUG_ERROR(FMT, ...) TSK_DEBUG_ERROR("[MSDK H264 Codec] " FMT, ##__VA_ARGS__)
+#define INTEL_DEBUG_FATAL(FMT, ...) TSK_DEBUG_FATAL("[MSDK H264 Codec] " FMT, ##__VA_ARGS__)
+
+#define INTEL_ALIGN16(i) ((((i) + 15) >> 4) << 4)
+#define INTEL_ALIGN32(i) (((mfxU32)((i)+31)) & (~ (mfxU32)31))
+#define INTEL_SAFE_RELEASE(X) {if ((X)) { (X)->Release(); (X) = NULL; }}
+
+#define INTEL_CHECK_STATUS(x) { mfxStatus __status__ = (x); if (__status__ != MFX_ERR_NONE) { INTEL_DEBUG_ERROR("Operation Failed (%d)", __status__); goto bail; } }
+#define INTEL_BREAK(msg) { INTEL_DEBUG_ERROR("%s", msg); goto bail; }
+
+#define INTEL_ENABLE_REALTIME 1
+
+static mfxIMPL __IntelDefaultImpl = MFX_IMPL_AUTO_ANY
+#if INTEL_DX11_D3D
+| MFX_IMPL_VIA_D3D11
+#endif
+;
+static mfxVersion __IntelDefaultVer = { 0, 1 };
+
+// TODO: Test against FFmpeg, CUDA, OpenH264 and Microsoft implementations
+// TODO: When Bandwidth change (or any other event) Reset() fails
+
+class IntelCodecEncoder;
+class IntelCodecDecoder;
+
+typedef struct tdav_codec_h264_intel_s
+{
+ TDAV_DECLARE_CODEC_H264_COMMON;
+
+ MFXVideoSession* mfxSession;
+
+ // DX11_D3D
+#if INTEL_DX11_D3D
+ mfxFrameAllocResponse D3D11SavedAllocResponses[2/*Encode=0, Decode=1*/];
+ mfxFrameAllocator D3D11Allocator;
+
+ CComPtr<ID3D11Device> pD3D11Device;
+ CComPtr<ID3D11DeviceContext> pD3D11Ctx;
+ CComPtr<IDXGIAdapter>pAdapter;
+ CComPtr<IDXGIFactory1> pDXGIFactory;
+ CComPtr<IDXGIAdapter> hAdapter;
+#endif
+
+ // Encoder
+ struct{
+ IntelCodecEncoder *pInst;
+ int64_t frame_count;
+ tsk_bool_t force_idr;
+ int rotation;
+ int neg_width;
+ int neg_height;
+ int neg_fps;
+ int max_bitrate_bps;
+ } encoder;
+
+ // decoder
+ struct{
+ IntelCodecDecoder *pInst;
+ } decoder;
+}
+tdav_codec_h264_intel_t;
+
+#if !defined(INTEL_H264_GOP_SIZE_IN_SECONDS)
+# define INTEL_H264_GOP_SIZE_IN_SECONDS 25
+#endif
+
+static int tdav_codec_h264_intel_init(tdav_codec_h264_intel_t* self, profile_idc_t profile);
+static int tdav_codec_h264_intel_deinit(tdav_codec_h264_intel_t* self);
+static int tdav_codec_h264_intel_open_encoder(tdav_codec_h264_intel_t* self);
+static int tdav_codec_h264_intel_close_encoder(tdav_codec_h264_intel_t* self);
+static int tdav_codec_h264_intel_open_decoder(tdav_codec_h264_intel_t* self);
+static int tdav_codec_h264_intel_close_decoder(tdav_codec_h264_intel_t* self);
+
+#if INTEL_DX11_D3D
+#define D3D11_WILL_READ 0x1000
+#define D3D11_WILL_WRITE 0x2000
+
+typedef struct {
+ mfxMemId memId;
+ mfxMemId memIdStage;
+ mfxU16 rw;
+} CustomMemId;
+
+const struct {
+ mfxIMPL impl; // actual implementation
+ mfxU32 adapterID; // device adapter number
+} implTypes[] = {
+ { MFX_IMPL_HARDWARE, 0 },
+ { MFX_IMPL_HARDWARE2, 1 },
+ { MFX_IMPL_HARDWARE3, 2 },
+ { MFX_IMPL_HARDWARE4, 3 }
+};
+
+static mfxStatus D3D11_CreateHWDevice(mfxHDL pthis, mfxSession session, mfxHDL* deviceHandle, HWND hWnd);
+static void D3D11_CleanupHWDevice(mfxHDL pthis);
+static void D3D11_SetHWDeviceContext(mfxHDL pthis, CComPtr<ID3D11DeviceContext> devCtx);
+
+// Intel Media SDK memory allocator entrypoints....
+// - A slightly different allocation procedure is used for encode, decode and VPP
+static mfxStatus D3D11_SimpleAlloc(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response);
+static mfxStatus D3D11_SimpleLock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr);
+static mfxStatus D3D11_SimpleUnlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr);
+static mfxStatus D3D11_SimpleGethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle);
+static mfxStatus D3D11_SimpleFree(mfxHDL pthis, mfxFrameAllocResponse *response);
+#endif /* INTEL_DX11_D3D */
+
+//
+// IntelCodec
+//
+class IntelCodec
+{
+protected:
+ IntelCodec(MFXVideoSession* pSession)
+ : m_bOpened(false)
+ , m_pSession(pSession)
+ , n_nNumSurfaces(0)
+ , m_nSurfaceWidth(0)
+ , m_nSurfaceHeight(0)
+ , m_nSurfaceBitsPerPixel(0)
+ , m_nSurfaceSize(0)
+ , m_pSurfaceBuffers(NULL)
+ , m_ppSurfacePtrs(NULL)
+ {
+ memset(&m_sBitstream, 0, sizeof(m_sBitstream));
+ memset(&m_sParamReq, 0, sizeof(m_sParamReq));
+ memset(&m_sParamSel, 0, sizeof(m_sParamSel));
+ memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest));
+ }
+public:
+ virtual ~IntelCodec()
+ {
+ Close();
+ }
+ virtual mfxStatus Open(struct tdav_codec_h264_intel_s* pWrappedCodec) = 0;
+
+ virtual mfxStatus Close()
+ {
+ DeAllocSurfaces();
+ DeAllocateBitstream();
+
+ memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest));
+
+ m_bOpened = false;
+
+ return MFX_ERR_NONE;
+ }
+
+protected:
+ int GetFreeSurfaceIndex()
+ {
+ if (m_ppSurfacePtrs)
+ {
+ for (mfxU16 i = 0; i < n_nNumSurfaces; i++)
+ {
+ if (0 == m_ppSurfacePtrs[i]->Data.Locked)
+ {
+ return i;
+ }
+ }
+ }
+ return MFX_ERR_NOT_FOUND;
+ }
+
+ mfxStatus ReadPlaneData(mfxU16 w, mfxU16 h, mfxU8 *buf, mfxU8 *ptr, mfxU16 pitch, mfxU16 offset, const mfxU8* &src)
+ {
+ for (mfxU16 i = 0; i < h; i++)
+ {
+ memcpy(buf, src, w);
+ src += w;
+
+ for (mfxU16 j = 0; j < w; j++)
+ ptr[i * pitch + j * 2 + offset] = buf[j];
+ }
+ return MFX_ERR_NONE;
+ }
+
+ mfxStatus LoadRawFrame(int nSurfaceIndex, const mfxU8* src)
+ {
+ mfxFrameSurface1* pSurface = (m_ppSurfacePtrs && nSurfaceIndex >= 0 && nSurfaceIndex < n_nNumSurfaces) ? m_ppSurfacePtrs[nSurfaceIndex] : NULL;
+ if (!pSurface)
+ {
+ INTEL_DEBUG_ERROR("Failed to find surface at index=%d", nSurfaceIndex);
+ return MFX_ERR_NOT_FOUND;
+ }
+
+ mfxStatus sts = MFX_ERR_NONE;
+ mfxU16 w, h, i, pitch;
+ mfxU8 *ptr;
+ mfxFrameInfo* pInfo = &pSurface->Info;
+ mfxFrameData* pData = &pSurface->Data;
+
+ if (pInfo->CropH > 0 && pInfo->CropW > 0) {
+ w = pInfo->CropW;
+ h = pInfo->CropH;
+ }
+ else {
+ w = pInfo->Width;
+ h = pInfo->Height;
+ }
+
+ pitch = pData->Pitch;
+ ptr = pData->Y + pInfo->CropX + pInfo->CropY * pData->Pitch;
+
+ // read luminance plane
+ for (i = 0; i < h; i++)
+ {
+ memcpy(ptr + i * pitch, src, w);
+ src += w;
+ }
+
+ mfxU8 buf[2048]; // maximum supported chroma width for nv12
+ w /= 2;
+ h /= 2;
+ ptr = pData->UV + pInfo->CropX + (pInfo->CropY / 2) * pitch;
+ if (w > 2048)
+ return MFX_ERR_UNSUPPORTED;
+
+ // load U
+ sts = ReadPlaneData(w, h, buf, ptr, pitch, 0, src);
+ if (MFX_ERR_NONE != sts) return sts;
+ // load V
+ sts = ReadPlaneData(w, h, buf, ptr, pitch, 1, src);
+ if (MFX_ERR_NONE != sts) return sts;
+
+ return MFX_ERR_NONE;
+ }
+
+ virtual mfxStatus AllocSurfaces(mfxU16 nNumSurfaces, mfxU16 nSurfaceWidth, mfxU16 nSurfaceHeight, const mfxFrameInfo* pcFrameInfo)
+ {
+ mfxStatus status = MFX_ERR_UNKNOWN;
+
+ INTEL_DEBUG_INFO("Alloc surfaces: num=%u, width=%u, height=%u", nNumSurfaces, nSurfaceWidth, nSurfaceHeight);
+
+ DeAllocSurfaces();
+
+ n_nNumSurfaces = nNumSurfaces;
+ m_nSurfaceWidth = (mfxU16)INTEL_ALIGN32(nSurfaceWidth);
+ m_nSurfaceHeight = (mfxU16)INTEL_ALIGN32(nSurfaceHeight);
+ m_nSurfaceBitsPerPixel = 12; // NV12 format is a 12 bits per pixel format
+ m_nSurfaceSize = m_nSurfaceWidth * m_nSurfaceHeight * m_nSurfaceBitsPerPixel / 8;
+
+#if !INTEL_DX11_D3D
+ if (!(m_pSurfaceBuffers = (mfxU8 *)new mfxU8[m_nSurfaceSize * n_nNumSurfaces]))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+#endif
+
+ if (!(m_ppSurfacePtrs = new mfxFrameSurface1*[n_nNumSurfaces]))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+ for (mfxU16 i = 0; i < n_nNumSurfaces; i++)
+ {
+ if (!(m_ppSurfacePtrs[i] = new mfxFrameSurface1))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+ memset(m_ppSurfacePtrs[i], 0, sizeof(mfxFrameSurface1));
+ memcpy(&(m_ppSurfacePtrs[i]->Info), pcFrameInfo, sizeof(mfxFrameInfo));
+#if INTEL_DX11_D3D
+ m_ppSurfacePtrs[i]->Data.MemId = m_sD3D11Response.mids[i]; // MID (memory id) represent one D3D NV12 surface
+#else
+ m_ppSurfacePtrs[i]->Data.Y = &m_pSurfaceBuffers[m_nSurfaceSize * i];
+ m_ppSurfacePtrs[i]->Data.U = m_ppSurfacePtrs[i]->Data.Y + m_nSurfaceWidth * m_nSurfaceHeight;
+ m_ppSurfacePtrs[i]->Data.V = m_ppSurfacePtrs[i]->Data.U + 1;
+ m_ppSurfacePtrs[i]->Data.Pitch = m_nSurfaceWidth;
+#endif
+ }
+
+ return MFX_ERR_NONE;
+
+ bail:
+ DeAllocSurfaces();
+ return status;
+ }
+
+ mfxStatus AllocateBitstream(mfxU32 nMaxLength)
+ {
+ DeAllocateBitstream();
+
+ m_sBitstream.MaxLength = nMaxLength;
+ if (!(m_sBitstream.Data = new mfxU8[nMaxLength]))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+
+ return MFX_ERR_NONE;
+
+ bail:
+ return MFX_ERR_MEMORY_ALLOC;
+ }
+private:
+ mfxStatus DeAllocSurfaces()
+ {
+ if (m_ppSurfacePtrs)
+ {
+ for (mfxU16 i = 0; i < n_nNumSurfaces; i++)
+ {
+ if (m_ppSurfacePtrs[i])
+ {
+ delete m_ppSurfacePtrs[i];
+ }
+ }
+ delete[] m_ppSurfacePtrs;
+ m_ppSurfacePtrs = NULL;
+ }
+ n_nNumSurfaces = 0;
+
+ if (m_pSurfaceBuffers)
+ {
+ delete[] m_pSurfaceBuffers;
+ m_pSurfaceBuffers = NULL;
+ }
+
+ m_nSurfaceWidth = 0;
+ m_nSurfaceHeight = 0;
+ m_nSurfaceBitsPerPixel = 0;
+ m_nSurfaceSize = 0;
+
+ return MFX_ERR_NONE;
+ }
+
+ mfxStatus DeAllocateBitstream()
+ {
+ if (m_sBitstream.Data)
+ {
+ delete[]m_sBitstream.Data;
+ }
+ memset(&m_sBitstream, 0, sizeof(m_sBitstream));
+
+ return MFX_ERR_NONE;
+ }
+
+
+protected:
+ bool m_bOpened;
+ MFXVideoSession* m_pSession;
+ mfxU16 n_nNumSurfaces;
+ mfxU16 m_nSurfaceWidth;
+ mfxU16 m_nSurfaceHeight;
+ mfxU8 m_nSurfaceBitsPerPixel;
+ mfxU32 m_nSurfaceSize;
+ mfxU8* m_pSurfaceBuffers; // mfxU8[];
+ mfxFrameSurface1** m_ppSurfacePtrs; // mfxFrameSurface1[]
+ mfxBitstream m_sBitstream;
+ mfxVideoParam m_sParamReq; // requested params
+ mfxVideoParam m_sParamSel; // selected params
+ mfxFrameAllocRequest m_sAllocRequest;
+#if INTEL_DX11_D3D
+ mfxFrameAllocResponse m_sD3D11Response;
+#endif
+};
+
+
+//
+// IntelCodecEncoder
+//
+class IntelCodecEncoder : public IntelCodec
+{
+public:
+ IntelCodecEncoder(MFXVideoSession* pSession)
+ : IntelCodec(pSession)
+ , m_Inst(*pSession)
+ {
+ memset(&m_sFrameCtrl, 0, sizeof(m_sFrameCtrl));
+ }
+ virtual ~IntelCodecEncoder()
+ {
+ Close();
+ }
+
+ virtual mfxStatus Close()
+ {
+ m_Inst.Close();
+ memset(&m_sFrameCtrl, 0, sizeof(m_sFrameCtrl));
+ return IntelCodec::Close();
+ }
+
+ mfxStatus Reset()
+ {
+ if (m_bOpened)
+ {
+ return m_Inst.Reset(&m_sParamSel);
+ }
+ return MFX_ERR_NONE;
+ }
+
+ mfxStatus Open(struct tdav_codec_h264_intel_s* pWrappedCodec)
+ {
+ int32_t max_bw_kpbs;
+ tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec;
+ mfxStatus status = MFX_ERR_UNKNOWN;
+ mfxU16 uSelWidth, uSelHeight;
+
+ pWrappedCodec->encoder.neg_width = (pWrappedCodec->encoder.rotation == 90 || pWrappedCodec->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.height : TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.width;
+ pWrappedCodec->encoder.neg_height = (pWrappedCodec->encoder.rotation == 90 || pWrappedCodec->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.width : TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.height;
+ pWrappedCodec->encoder.neg_fps = TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.fps;
+ max_bw_kpbs = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(pWrappedCodec->encoder.neg_width, pWrappedCodec->encoder.neg_height, pWrappedCodec->encoder.neg_fps),
+ TMEDIA_CODEC(pWrappedCodec)->bandwidth_max_upload
+ );
+ pWrappedCodec->encoder.max_bitrate_bps = (max_bw_kpbs * 1024);
+
+ INTEL_DEBUG_INFO("neg_width=%d, neg_height=%d, neg_fps=%d, max_bitrate_bps=%d",
+ pWrappedCodec->encoder.neg_width,
+ pWrappedCodec->encoder.neg_height,
+ pWrappedCodec->encoder.neg_fps,
+ pWrappedCodec->encoder.max_bitrate_bps
+ );
+
+ // Initialize encoder parameters
+ memset(&m_sParamReq, 0, sizeof(m_sParamReq));
+ m_sParamReq.mfx.CodecId = MFX_CODEC_AVC;
+ m_sParamReq.mfx.CodecProfile = pWrappedCodecCommon->profile == profile_idc_main ? MFX_PROFILE_AVC_MAIN : MFX_PROFILE_AVC_BASELINE;
+ m_sParamReq.mfx.CodecLevel = (mfxU16)pWrappedCodecCommon->level;
+ // TODO: Update "CodecProfile" based on "common->profile_iop"
+ m_sParamReq.mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED;
+ m_sParamReq.mfx.TargetKbps = max_bw_kpbs;
+ m_sParamReq.mfx.RateControlMethod = MFX_RATECONTROL_CBR;
+ m_sParamReq.mfx.IdrInterval = (pWrappedCodec->encoder.neg_fps * INTEL_H264_GOP_SIZE_IN_SECONDS);
+ m_sParamReq.mfx.FrameInfo.FrameRateExtN = pWrappedCodec->encoder.neg_fps;
+ m_sParamReq.mfx.FrameInfo.FrameRateExtD = 1;
+ m_sParamReq.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
+ m_sParamReq.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
+ m_sParamReq.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
+ m_sParamReq.mfx.FrameInfo.CropX = 0;
+ m_sParamReq.mfx.FrameInfo.CropY = 0;
+ m_sParamReq.mfx.FrameInfo.CropW = pWrappedCodec->encoder.neg_width;
+ m_sParamReq.mfx.FrameInfo.CropH = pWrappedCodec->encoder.neg_height;
+ m_sParamReq.mfx.FrameInfo.Width = INTEL_ALIGN16(pWrappedCodec->encoder.neg_width); // must be a multiple of 16
+ m_sParamReq.mfx.FrameInfo.Height = INTEL_ALIGN16(pWrappedCodec->encoder.neg_height); // must be a multiple of 16
+#if INTEL_DX11_D3D
+ m_sParamReq.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
+#else
+ m_sParamReq.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
+#endif
+
+ memset(&m_sOpt2MaxFrameSize, 0, sizeof(m_sOpt2MaxFrameSize));
+ m_sOpt2MaxFrameSize.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
+ m_sOpt2MaxFrameSize.Header.BufferSz = sizeof(m_sOpt2MaxFrameSize);
+ m_sOpt2MaxFrameSize.MaxSliceSize = (H264_RTP_PAYLOAD_SIZE - 100);
+ m_sOpt2MaxFrameSize.RepeatPPS = MFX_CODINGOPTION_OFF;
+ m_pExtendedBuffers[0] = (mfxExtBuffer*)&m_sOpt2MaxFrameSize;
+#if INTEL_ENABLE_REALTIME
+ m_sParamReq.AsyncDepth = 1; // limits internal frame buffering
+ m_sParamReq.mfx.GopRefDist = 1; // No B-Frames
+ m_sParamReq.mfx.NumRefFrame = 1;
+ memset(&m_sOptLowLatency, 0, sizeof(m_sOptLowLatency));
+ m_sOptLowLatency.Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
+ m_sOptLowLatency.Header.BufferSz = sizeof(m_sOptLowLatency);
+ m_sOptLowLatency.MaxDecFrameBuffering = 1;
+ m_pExtendedBuffers[1] = (mfxExtBuffer*)&m_sOptLowLatency;
+ m_sParamReq.NumExtParam = 2;
+#else
+ m_sParamReq.NumExtParam = 1;
+#endif
+ m_sParamReq.ExtParam = m_pExtendedBuffers;
+
+ // Check parameters
+ status = m_Inst.Query(&m_sParamReq, &m_sParamReq);
+ if (status != MFX_ERR_NONE && status != MFX_WRN_INCOMPATIBLE_VIDEO_PARAM /* Best one will be selected by the encoder */) {
+ INTEL_CHECK_STATUS(status);
+ }
+ if (m_sOpt2MaxFrameSize.MaxSliceSize == 0)
+ {
+ INTEL_DEBUG_INFO("The encoder doesn't support setting 'MaxSliceSize' :(");
+ }
+
+ // Query number required surfaces for encoder
+ memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest));
+ INTEL_CHECK_STATUS(status = m_Inst.QueryIOSurf(&m_sParamReq, &m_sAllocRequest));
+ INTEL_DEBUG_INFO("nEncSurfNum = %hu", m_sAllocRequest.NumFrameSuggested);
+#if INTEL_DX11_D3D
+ m_sAllocRequest.Type |= D3D11_WILL_WRITE; // Hint to DX11 memory handler that application will write data to input surfaces
+#endif
+
+ // Allocate surfaces for encoder
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodec->D3D11Allocator.Alloc(pWrappedCodec->D3D11Allocator.pthis, &m_sAllocRequest, &m_sD3D11Response));
+ if (m_sD3D11Response.NumFrameActual == 0)
+ {
+ INTEL_CHECK_STATUS(status = MFX_ERR_UNKNOWN);
+ }
+ INTEL_CHECK_STATUS(status = AllocSurfaces(m_sD3D11Response.NumFrameActual, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo));
+#else
+ INTEL_CHECK_STATUS(status = AllocSurfaces(m_sAllocRequest.NumFrameSuggested, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo));
+#endif
+
+ // Initialize the Media SDK encoder
+ status = m_Inst.Init(&m_sParamReq);
+ if (status != MFX_ERR_NONE && status != MFX_WRN_PARTIAL_ACCELERATION) {
+ INTEL_CHECK_STATUS(status);
+ }
+ INTEL_DEBUG_INFO("Encoder->Init() returned: %d", status);
+
+ // Retrieve video parameters selected by encoder.
+ memset(&m_sParamSel, 0, sizeof(m_sParamSel));
+ INTEL_CHECK_STATUS(status = m_Inst.GetVideoParam(&m_sParamSel));
+ INTEL_DEBUG_INFO("sel_width=%u.crop=%u, sel_height=%u.crop=%u, sel_fps=%u/%u",
+ m_sParamSel.mfx.FrameInfo.Width, m_sParamSel.mfx.FrameInfo.CropW,
+ m_sParamSel.mfx.FrameInfo.Height, m_sParamSel.mfx.FrameInfo.CropH,
+ m_sParamReq.mfx.FrameInfo.FrameRateExtN,
+ m_sParamReq.mfx.FrameInfo.FrameRateExtD
+ );
+ if (m_sParamSel.mfx.FrameInfo.CropW > 0 && m_sParamSel.mfx.FrameInfo.CropH > 0)
+ {
+ uSelWidth = m_sParamSel.mfx.FrameInfo.CropW;
+ uSelHeight = m_sParamSel.mfx.FrameInfo.CropH;
+ }
+ else
+ {
+ uSelWidth = m_sParamSel.mfx.FrameInfo.Width;
+ uSelHeight = m_sParamSel.mfx.FrameInfo.Height;
+ }
+ if (pWrappedCodec->encoder.neg_width != uSelWidth || pWrappedCodec->encoder.neg_height != uSelHeight) {
+ INTEL_DEBUG_INFO("Encoder neg size <> sel size: %dx%d<>%dx%d", pWrappedCodec->encoder.neg_width, pWrappedCodec->encoder.neg_height, uSelWidth, uSelHeight);
+ pWrappedCodec->encoder.neg_width = uSelWidth;
+ pWrappedCodec->encoder.neg_height = uSelHeight;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.width = pWrappedCodec->encoder.neg_width;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.height = pWrappedCodec->encoder.neg_height;
+ }
+
+ // Allocate BitStream
+ INTEL_CHECK_STATUS(status = AllocateBitstream(m_sParamSel.mfx.BufferSizeInKB * 1000));
+
+ m_bOpened = true;
+ return MFX_ERR_NONE;
+
+ bail:
+ Close();
+ return status;
+ }
+
+ mfxStatus UpdateBandwidth(bool bUp, mfxU16 max)
+ {
+ if (bUp)
+ {
+ m_sParamSel.mfx.TargetKbps = TSK_CLAMP(0, (mfxU16)((m_sParamSel.mfx.TargetKbps * 3) >> 1), max);
+ }
+ else
+ {
+ m_sParamSel.mfx.TargetKbps = TSK_CLAMP(0, (mfxU16)((m_sParamSel.mfx.TargetKbps << 1) / 3), max);
+ }
+ m_sParamReq.mfx.TargetKbps = m_sParamSel.mfx.TargetKbps;
+ INTEL_DEBUG_INFO("Setting new target bandwidth to %ukbps", m_sParamSel.mfx.TargetKbps);
+ return m_Inst.Reset(&m_sParamSel);
+ }
+
+ mfxStatus SetMaxBandwidth(mfxU16 max)
+ {
+ m_sParamSel.mfx.TargetKbps = TSK_CLAMP(0, m_sParamSel.mfx.TargetKbps, max);
+ m_sParamReq.mfx.TargetKbps = m_sParamSel.mfx.TargetKbps;
+ INTEL_DEBUG_INFO("Setting new target bandwidth to %ukbps", m_sParamSel.mfx.TargetKbps);
+ return m_Inst.Reset(&m_sParamSel);
+ }
+
+ mfxStatus Encode(struct tmedia_codec_s* pWrappedCodec, const mfxU8* pcInDataPtr, mfxU32 nInDataSize)
+ {
+ tdav_codec_h264_intel_t* pWrappedCodecH264 = (tdav_codec_h264_intel_t*)pWrappedCodec;
+ tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec;
+ mfxU32 nInDataXSize;
+ tsk_bool_t bSendIDR;
+ int nEncSurfIdx = 0;
+ mfxSyncPoint syncp;
+ mfxStatus status = MFX_ERR_UNKNOWN;
+
+ if (!pWrappedCodec || !pcInDataPtr || !nInDataSize) {
+ INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR);
+ }
+
+ if (!m_bOpened) {
+ INTEL_CHECK_STATUS(MFX_ERR_NOT_INITIALIZED);
+ }
+
+ nInDataXSize = (pWrappedCodecH264->encoder.neg_width * pWrappedCodecH264->encoder.neg_height * 3) >> 1;
+ if (nInDataXSize != nInDataSize)
+ {
+ /* guard */
+ INTEL_DEBUG_ERROR("Invalid size: %u<>%u", nInDataXSize, nInDataSize);
+ goto bail;
+ }
+
+ bSendIDR = (pWrappedCodecH264->encoder.frame_count++ == 0 || pWrappedCodecH264->encoder.force_idr);
+
+ nEncSurfIdx = GetFreeSurfaceIndex();
+ if (MFX_ERR_NOT_FOUND == nEncSurfIdx)
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+
+ // Surface locking required when read/write D3D surfaces
+
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Lock(pWrappedCodecH264->D3D11Allocator.pthis, m_ppSurfacePtrs[nEncSurfIdx]->Data.MemId, &(m_ppSurfacePtrs[nEncSurfIdx]->Data)));
+#endif
+
+ INTEL_CHECK_STATUS(status = LoadRawFrame(nEncSurfIdx, pcInDataPtr));
+
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Unlock(pWrappedCodecH264->D3D11Allocator.pthis, m_ppSurfacePtrs[nEncSurfIdx]->Data.MemId, &(m_ppSurfacePtrs[nEncSurfIdx]->Data)));
+#endif
+
+ m_sFrameCtrl.FrameType = bSendIDR ? (MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF | MFX_FRAMETYPE_IDR) : MFX_FRAMETYPE_UNKNOWN;
+
+ //
+ // Stage 1: Main encoding loop
+ //
+ do
+ {
+ for (;;)
+ {
+ // Encode a frame asychronously (returns immediately)
+ status = m_Inst.EncodeFrameAsync(&m_sFrameCtrl, m_ppSurfacePtrs[nEncSurfIdx], &m_sBitstream, &syncp);
+
+ if (MFX_ERR_NONE < status && !syncp) // Repeat the call if warning and no output
+ {
+ if (MFX_WRN_DEVICE_BUSY == status)
+ {
+ tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call
+ }
+ }
+ else if (MFX_ERR_NONE < status && syncp)
+ {
+ status = MFX_ERR_NONE; // Ignore warnings if output is available
+ break;
+ }
+ else if (MFX_ERR_NOT_ENOUGH_BUFFER == status)
+ {
+ // Allocate more bitstream buffer memory here if needed...
+ break;
+ }
+ else
+ {
+ if (status != MFX_ERR_MORE_DATA)
+ {
+ INTEL_CHECK_STATUS(status);
+ }
+ break;
+ }
+ }
+ if (MFX_ERR_NONE == status)
+ {
+ INTEL_CHECK_STATUS(m_pSession->SyncOperation(syncp, 60000)); // Synchronize. Wait until encoded frame is ready
+ if (m_sBitstream.DataLength > 0)
+ {
+ tdav_codec_h264_rtp_encap(pWrappedCodecCommon, (const uint8_t*)(m_sBitstream.Data + m_sBitstream.DataOffset), (tsk_size_t)m_sBitstream.DataLength);
+ m_sBitstream.DataLength = 0;
+ pWrappedCodecH264->encoder.force_idr = tsk_false; // reset
+ }
+ }
+ } while (0);
+
+ //
+ // Stage 2: Retrieve the buffered encoded frames
+ //
+ while (MFX_ERR_NONE <= status)
+ {
+ for (;;)
+ {
+ // Encode a frame asychronously (returns immediately)
+ status = m_Inst.EncodeFrameAsync(&m_sFrameCtrl, NULL, &m_sBitstream, &syncp);
+
+ if (MFX_ERR_NONE < status && !syncp) // Repeat the call if warning and no output
+ {
+ if (MFX_WRN_DEVICE_BUSY == status)
+ {
+ tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call
+ }
+ }
+ else if (MFX_ERR_NONE < status && syncp)
+ {
+ status = MFX_ERR_NONE; // Ignore warnings if output is available
+ break;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (MFX_ERR_NONE == status)
+ {
+ INTEL_CHECK_STATUS(m_pSession->SyncOperation(syncp, 60000)); // Synchronize. Wait until encoded frame is
+ if (m_sBitstream.DataLength > 0)
+ {
+ tdav_codec_h264_rtp_encap(pWrappedCodecCommon, (const uint8_t*)(m_sBitstream.Data + m_sBitstream.DataOffset), (tsk_size_t)m_sBitstream.DataLength);
+ m_sBitstream.DataLength = 0;
+ pWrappedCodecH264->encoder.force_idr = tsk_false; // reset
+ }
+ }
+ }
+
+ bail:
+ return MFX_ERR_NONE;
+ }
+
+private:
+ MFXVideoENCODE m_Inst;
+ mfxEncodeCtrl m_sFrameCtrl;
+ mfxExtCodingOption m_sOptLowLatency;
+ mfxExtCodingOption2 m_sOpt2MaxFrameSize;
+ mfxExtBuffer* m_pExtendedBuffers[2]; // Not allocated
+};
+
+
+//
+// IntelCodecDecoder
+//
+class IntelCodecDecoder : public IntelCodec
+{
+public:
+ IntelCodecDecoder(MFXVideoSession* pSession)
+ : IntelCodec(pSession)
+ , m_Inst(*pSession)
+ , m_pAccumulatorPtr(NULL)
+ , m_nAccumulatorSize(0)
+ , m_nAccumulatorPos(0)
+ , m_bInit(false)
+ {
+
+ }
+ virtual ~IntelCodecDecoder()
+ {
+ Close();
+ }
+
+ virtual mfxStatus Close()
+ {
+ m_Inst.Close();
+ TSK_FREE(m_pAccumulatorPtr);
+ m_nAccumulatorSize = 0;
+ m_nAccumulatorPos = 0;
+ m_bInit = false;
+ return IntelCodec::Close();
+ }
+
+ mfxStatus Open(struct tdav_codec_h264_intel_s* pWrappedCodec)
+ {
+ tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec;
+
+ INTEL_DEBUG_INFO("Decoder.Open width=%d, height=%d, fps=%d",
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.width,
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.height,
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.fps
+ );
+
+ // Allocation will be done each time we decode the SPS:PPS header
+
+ m_bOpened = true;
+ return MFX_ERR_NONE;
+ }
+
+ mfxU32 Decode(struct tmedia_codec_s* pWrappedCodec, const mfxU8* pcInDataPtr, mfxU32 nInDataSize, void **ppOutDataPtr, tsk_size_t *pOutDataMaxSize, const trtp_rtp_header_t* pcRtpHdr)
+ {
+ mfxU32 nRetSize = 0, nOutXSize;
+ mfxStatus status = MFX_ERR_NONE;
+ tsk_bool_t append_scp, end_of_unit;
+ tsk_bool_t sps_or_pps;
+ const uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size = 0, size_to_copy = 0;
+ bool bGotFrame = false;
+ mfxFrameSurface1* pmfxOutSurface = NULL;
+ static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
+ static tsk_size_t start_code_prefix_size = sizeof(H264_START_CODE_PREFIX);
+
+ tdav_codec_h264_intel_t* pWrappedCodecH264 = (tdav_codec_h264_intel_t*)pWrappedCodec;
+ tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec;
+
+ if (!pWrappedCodec || !pcInDataPtr || !nInDataSize || !ppOutDataPtr)
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR);
+ }
+ //INTEL_DEBUG_INFO("Size=%u", nInDataSize);
+ if (!m_bOpened)
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_NOT_INITIALIZED);
+ }
+
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ if (pcInDataPtr[0] & 0x80) // F ?== 1
+ {
+ /* reset accumulator */
+ m_nAccumulatorPos = 0;
+ INTEL_CHECK_STATUS(status = MFX_ERR_UNDEFINED_BEHAVIOR);
+ }
+
+ // New frame?
+ if (m_nLastRtpTimestamp != pcRtpHdr->timestamp)
+ {
+ m_nAccumulatorPos = 0;
+ m_nLastRtpTimestamp = pcRtpHdr->timestamp;
+ }
+
+ /* get payload */
+ if ((tdav_codec_h264_get_pay(pcInDataPtr, nInDataSize, (const void**)&pay_ptr, &pay_size, &append_scp, &end_of_unit) != 0) || !pay_ptr || !pay_size)
+ {
+ INTEL_BREAK("Depayloader failed to get H.264 content");
+ }
+#if 1 // TODO: MSDK cannot decode slices
+ end_of_unit = pcRtpHdr->marker;
+#endif
+ //append_scp = tsk_true;
+ size_to_copy = pay_size + (append_scp ? start_code_prefix_size : 0);
+ // whether it's SPS or PPS (append_scp is false for subsequent FUA chuncks)
+ sps_or_pps = append_scp && pay_ptr && ((pay_ptr[0] & 0x1F) == 7 || (pay_ptr[0] & 0x1F) == 8);
+
+ // start-accumulator
+ if (!m_pAccumulatorPtr)
+ {
+ if (size_to_copy > xmax_size)
+ {
+ INTEL_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", size_to_copy, xmax_size);
+ m_nAccumulatorPos = 0;
+ return 0;
+ }
+ if (!(m_pAccumulatorPtr = (mfxU8*)tsk_calloc(size_to_copy, sizeof(mfxU8))))
+ {
+ INTEL_DEBUG_ERROR("Failed to allocated new buffer");
+ m_nAccumulatorPos = 0;
+ return 0;
+ }
+ m_nAccumulatorSize = (mfxU32)size_to_copy;
+ }
+ if ((m_nAccumulatorPos + size_to_copy) >= xmax_size)
+ {
+ INTEL_DEBUG_ERROR("BufferOverflow");
+ m_nAccumulatorPos = 0;
+ return 0;
+ }
+ if ((m_nAccumulatorPos + size_to_copy) > m_nAccumulatorSize)
+ {
+ if (!(m_pAccumulatorPtr = (mfxU8*)tsk_realloc(m_pAccumulatorPtr, (m_nAccumulatorPos + size_to_copy))))
+ {
+ INTEL_DEBUG_ERROR("Failed to reallocated new buffer");
+ m_nAccumulatorPos = 0;
+ m_nAccumulatorSize = 0;
+ return 0;
+ }
+ m_nAccumulatorSize = (mfxU32)(m_nAccumulatorPos + size_to_copy);
+ }
+
+ if (append_scp)
+ {
+ memcpy(&m_pAccumulatorPtr[m_nAccumulatorPos], H264_START_CODE_PREFIX, start_code_prefix_size);
+ m_nAccumulatorPos += (mfxU32)start_code_prefix_size;
+ }
+ memcpy(&m_pAccumulatorPtr[m_nAccumulatorPos], pay_ptr, pay_size);
+ m_nAccumulatorPos += (mfxU32)pay_size;
+ // end-accumulator
+
+ if (/*rtp_hdr->marker*/end_of_unit)
+ {
+ /* decode the picture */
+ mfxU32 nOutWidth, nOutHeight;
+
+ // Decode a Unit
+ status = DecodeFrame(pWrappedCodecH264, m_pAccumulatorPtr, m_nAccumulatorPos, !!sps_or_pps, &pmfxOutSurface, bGotFrame);
+ if (status != MFX_ERR_NONE)
+ {
+ INTEL_DEBUG_WARN("DecodeFrame failed: %d", status);
+ goto bail;
+ }
+
+ // Do we have a complete frame?
+ if (!bGotFrame || !pmfxOutSurface)
+ {
+ goto bail;
+ }
+
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Lock(pWrappedCodecH264->D3D11Allocator.pthis, pmfxOutSurface->Data.MemId, &(pmfxOutSurface->Data)));
+#endif
+ if (!pmfxOutSurface->Data.Y || !pmfxOutSurface->Data.U || !pmfxOutSurface->Data.V)
+ {
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Unlock(pWrappedCodecH264->D3D11Allocator.pthis, pmfxOutSurface->Data.MemId, &(pmfxOutSurface->Data)));
+#endif
+ goto bail;
+ }
+
+ if (pmfxOutSurface->Info.CropW > 0 && pmfxOutSurface->Info.CropH > 0)
+ {
+ nOutWidth = pmfxOutSurface->Info.CropW;
+ nOutHeight = pmfxOutSurface->Info.CropH;
+ }
+ else
+ {
+ nOutWidth = pmfxOutSurface->Info.Width;
+ nOutHeight = pmfxOutSurface->Info.Height;
+ }
+
+ nOutXSize = (nOutWidth * nOutHeight * 3) >> 1; // I420
+ /* IDR ? */
+ if (((pay_ptr[0] & 0x1F) == 0x05) && TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback)
+ {
+ INTEL_DEBUG_INFO("Decoded H.264 IDR");
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.type = tmedia_video_decode_result_type_idr;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.proto_hdr = pcRtpHdr;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback(&TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result);
+ }
+ /* fill out */
+ if (*pOutDataMaxSize < nOutXSize)
+ {
+ if ((*ppOutDataPtr = tsk_realloc(*ppOutDataPtr, nOutXSize)))
+ {
+ *pOutDataMaxSize = nOutXSize;
+ }
+ else
+ {
+ *pOutDataMaxSize = 0;
+ return 0;
+ }
+ }
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.width = nOutWidth;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.height = nOutHeight;
+
+ /* layout picture */
+ INTEL_CHECK_STATUS(status = IntelCodecDecoder::LayoutPicture(pmfxOutSurface, (mfxU8 *)*ppOutDataPtr));
+ nRetSize = nOutXSize;
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Unlock(pWrappedCodecH264->D3D11Allocator.pthis, pmfxOutSurface->Data.MemId, &(pmfxOutSurface->Data)));
+#endif
+ } // else if(rtp_hdr->marker)
+
+ bail:
+ if (end_of_unit)
+ {
+ /* reset accumulator */
+ m_nAccumulatorPos = 0;
+ }
+
+ if (status != MFX_ERR_NONE)
+ {
+ INTEL_DEBUG_INFO("Failed to decode the buffer with error code =%d, size=%u, append=%s", status, m_nAccumulatorPos, append_scp ? "yes" : "no");
+ if (TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback)
+ {
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.proto_hdr = pcRtpHdr;
+ TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback(&TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result);
+ }
+ }
+ return nRetSize;
+ }
+
+private:
+#if 0
+ static mfxStatus WriteSection(mfxU8* plane, mfxU16 factor, mfxU16 chunksize, mfxFrameInfo *pInfo, mfxFrameData *pData, mfxU32 i, mfxU32 j, mfxU8 *pDstPtr)
+ {
+ memcpy(pDstPtr, plane + (pInfo->CropY * pData->Pitch / factor + pInfo->CropX) + i * pData->Pitch + j, chunksize);
+ return MFX_ERR_NONE;
+ }
+#else
+#define WriteSection(_plane, _factor, _chunksize, _pInfo, _pData, _i, _j, _pDstPtr) \
+ memcpy((_pDstPtr), (_plane) + ((_pInfo)->CropY * (_pData)->Pitch / (_factor) + (_pInfo)->CropX) + (_i) * (_pData)->Pitch + (_j), (_chunksize))
+#define WriteSection1(_plane, _factor, _pInfo, _pData, _i, _j, _pDstPtr) \
+ *(_pDstPtr) = *((_plane) + ((_pInfo)->CropY * (_pData)->Pitch / (_factor) + (_pInfo)->CropX) + (_i) * (_pData)->Pitch + (_j));
+#endif
+
+ static mfxStatus LayoutPicture(mfxFrameSurface1 *pSurface, mfxU8 *pDstPtr)
+ {
+#if 1 // ->YUV420
+ mfxFrameInfo *pInfo = &pSurface->Info;
+ mfxFrameData *pData = &pSurface->Data;
+ mfxU32 i, j, h, w;
+
+ if (pSurface->Info.CropW > 0 && pSurface->Info.CropH > 0)
+ {
+ w = pSurface->Info.CropW;
+ h = pSurface->Info.CropH;
+ }
+ else
+ {
+ w = pSurface->Info.Width;
+ h = pSurface->Info.Height;
+ }
+
+ for (i = 0; i < h; i++)
+ {
+ WriteSection(pData->Y, 1, w, pInfo, pData, i, 0, pDstPtr);
+ pDstPtr += w;
+ }
+
+ h >>= 1;
+ for (i = 0; i < h; i++)
+ {
+ for (j = 0; j < w; j += 2)
+ {
+ WriteSection1(pData->UV, 2, pInfo, pData, i, j, pDstPtr);
+ pDstPtr += 1;
+ }
+ }
+
+ for (i = 0; i < h; i++)
+ {
+ for (j = 1; j < w; j += 2)
+ {
+ WriteSection1(pData->UV, 2, pInfo, pData, i, j, pDstPtr);
+ pDstPtr += 1;
+ }
+ }
+#elif 1 // ->NV12
+ mfxFrameInfo *pInfo = &pSurface->Info;
+ mfxFrameData *pData = &pSurface->Data;
+ mfxU32 i, j, h, w;
+
+ if (pSurface->Info.CropW > 0 && pSurface->Info.CropH > 0)
+ {
+ w = pSurface->Info.CropW;
+ h = pSurface->Info.CropH;
+ }
+ else
+ {
+ w = pSurface->Info.Width;
+ h = pSurface->Info.Height;
+ }
+
+ for (i = 0; i < h; i++)
+ {
+ WriteSection(pData->Y, 1, w, pInfo, pData, i, 0, pDstPtr);
+ pDstPtr += w;
+ }
+
+ h >>= 1;
+ for (i = 0; i < h; i++)
+ {
+ for (j = 0; j < w; j += 2)
+ {
+ WriteSection1(pData->UV, 2, pInfo, pData, i, j, &pDstPtr[0]);
+ WriteSection1(pData->UV, 2, pInfo, pData, i, j + 1, &pDstPtr[1]);
+ pDstPtr += 2;
+ }
+ }
+#endif
+
+ return MFX_ERR_NONE;
+ }
+
+ mfxStatus DecodeFrame(struct tdav_codec_h264_intel_s* pWrappedCodec, const mfxU8* pcInDataPtr, mfxU32 nInDataSize, bool bSpsOrPps, mfxFrameSurface1** ppmfxOutSurface, bool &bGotFrame)
+ {
+ mfxStatus status = MFX_ERR_NONE;
+ int nSurfaceIndex;
+ mfxSyncPoint syncp;
+ bGotFrame = false;
+ *ppmfxOutSurface = NULL;
+ mfxFrameSurface1* pmfxOutSurface = NULL;
+
+#if 0
+ if (!bSpsOrPps && !m_bInit)
+ {
+ INTEL_CHECK_STATUS(status = MFX_ERR_NOT_INITIALIZED);
+ }
+#endif
+
+ if (m_sBitstream.DataLength < nInDataSize)
+ {
+ INTEL_CHECK_STATUS(status = AllocateBitstream(nInDataSize));
+ }
+ memcpy(m_sBitstream.Data, pcInDataPtr, nInDataSize);
+ m_sBitstream.DataOffset = 0;
+ m_sBitstream.DataLength = nInDataSize;
+ m_sBitstream.DataFlag = MFX_BITSTREAM_COMPLETE_FRAME;
+
+ if (bSpsOrPps || !m_bInit)
+ {
+ memset(&m_sParamReq, 0, sizeof(m_sParamReq));
+ m_sParamReq.mfx.CodecId = MFX_CODEC_AVC;
+ m_sParamReq.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
+ m_sParamReq.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
+#if INTEL_DX11_D3D
+ m_sParamReq.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
+#else
+ m_sParamReq.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
+#endif
+ m_sParamReq.AsyncDepth = 1; // Low latency: limits internal frame buffering
+
+ status = m_Inst.DecodeHeader(&m_sBitstream, &m_sParamReq);
+ if (status == MFX_WRN_PARTIAL_ACCELERATION)
+ {
+ status = MFX_ERR_NONE;
+ }
+ INTEL_CHECK_STATUS(status);
+
+ memcpy(&m_sParamSel, &m_sParamReq, sizeof(m_sParamSel));
+
+ // Check parameters
+ status = m_Inst.Query(&m_sParamReq, &m_sParamReq);
+ if (status != MFX_ERR_NONE && status != MFX_WRN_INCOMPATIBLE_VIDEO_PARAM /* Best one will be selected by the encoder */)
+ {
+ INTEL_CHECK_STATUS(status);
+ }
+
+ // Query number required surfaces for encoder
+ memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest));
+ INTEL_CHECK_STATUS(status = m_Inst.QueryIOSurf(&m_sParamReq, &m_sAllocRequest));
+#if INTEL_DX11_D3D
+ m_sAllocRequest.Type |= D3D11_WILL_READ; // Hint to DX11 memory handler that application will read data from output surfaces
+#endif
+
+ // Allocate surfaces for decoder
+#if INTEL_DX11_D3D
+ INTEL_CHECK_STATUS(status = pWrappedCodec->D3D11Allocator.Alloc(pWrappedCodec->D3D11Allocator.pthis, &m_sAllocRequest, &m_sD3D11Response));
+ if (m_sD3D11Response.NumFrameActual == 0)
+ {
+ INTEL_CHECK_STATUS(status = MFX_ERR_UNKNOWN);
+ }
+ INTEL_DEBUG_INFO("nEncSurfNum = %hu", m_sD3D11Response.NumFrameActual);
+ INTEL_CHECK_STATUS(status = AllocSurfaces(m_sD3D11Response.NumFrameActual, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo));
+#else
+ INTEL_DEBUG_INFO("nEncSurfNum = %hu", m_sAllocRequest.NumFrameSuggested);
+ INTEL_CHECK_STATUS(status = AllocSurfaces(m_sAllocRequest.NumFrameSuggested, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo));
+#endif
+ // Initialize the Media SDK decoder
+ status = m_Inst.Init(&m_sParamReq);
+ if (status != MFX_ERR_NONE && status != MFX_WRN_PARTIAL_ACCELERATION)
+ {
+ INTEL_CHECK_STATUS(status);
+ }
+ INTEL_DEBUG_INFO("Decoder->Init() returned: %d", status);
+
+ m_bInit = true;
+ }
+
+ //
+ // Stage 1: Main decoding loop
+ //
+ while (MFX_ERR_NONE <= status || MFX_ERR_MORE_DATA == status || MFX_ERR_MORE_SURFACE == status)
+ {
+ if (MFX_WRN_DEVICE_BUSY == status)
+ {
+ tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call to DecodeFrameAsync
+ }
+
+ if (MFX_ERR_MORE_DATA == status)
+ {
+ return MFX_ERR_NONE;
+ }
+
+ if (MFX_ERR_MORE_SURFACE == status || MFX_ERR_NONE == status)
+ {
+ nSurfaceIndex = GetFreeSurfaceIndex(); // Find free frame surface
+ if (MFX_ERR_NOT_FOUND == nSurfaceIndex)
+ {
+ INTEL_CHECK_STATUS((status = MFX_ERR_MEMORY_ALLOC));
+ }
+ }
+
+ // Decode a frame asychronously (returns immediately)
+ // - If input bitstream contains multiple frames DecodeFrameAsync will start decoding multiple frames, and remove them from bitstream
+ status = m_Inst.DecodeFrameAsync(&m_sBitstream, m_ppSurfacePtrs[nSurfaceIndex], &pmfxOutSurface, &syncp);
+
+ // Ignore warnings if output is available,
+ // if no output and no action required just repeat the DecodeFrameAsync call
+ if (MFX_ERR_NONE < status && syncp)
+ {
+ status = MFX_ERR_NONE;
+ }
+
+ if (MFX_ERR_NONE == status)
+ {
+ status = m_pSession->SyncOperation(syncp, 60000); // Synchronize. Wait until decoded frame is ready
+ }
+
+ if (MFX_ERR_NONE == status)
+ {
+ bGotFrame = true;
+ if (pmfxOutSurface)
+ {
+ *ppmfxOutSurface = pmfxOutSurface;
+ }
+ }
+ }
+
+ //
+ // Stage 2: Retrieve the buffered decoded frames
+ //
+ while (MFX_ERR_NONE <= status || MFX_ERR_MORE_SURFACE == status)
+ {
+ if (MFX_WRN_DEVICE_BUSY == status)
+ {
+ tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call to DecodeFrameAsync
+ }
+
+ nSurfaceIndex = GetFreeSurfaceIndex(); // Find free frame surface
+ if (MFX_ERR_NOT_FOUND == nSurfaceIndex)
+ {
+ INTEL_CHECK_STATUS((status = MFX_ERR_MEMORY_ALLOC));
+ }
+
+ // Decode a frame asychronously (returns immediately)
+ status = m_Inst.DecodeFrameAsync(NULL, m_ppSurfacePtrs[nSurfaceIndex], ppmfxOutSurface, &syncp);
+
+ // Ignore warnings if output is available,
+ // if no output and no action required just repeat the DecodeFrameAsync call
+ if (MFX_ERR_NONE < status && syncp)
+ {
+ status = MFX_ERR_NONE;
+ }
+
+ if (MFX_ERR_NONE == status)
+ {
+ status = m_pSession->SyncOperation(syncp, 60000); // Synchronize. Waits until decoded frame is ready
+ }
+
+ if (MFX_ERR_NONE == status)
+ {
+ bGotFrame = true;
+ if (pmfxOutSurface)
+ {
+ *ppmfxOutSurface = pmfxOutSurface;
+ }
+ }
+ }
+
+ status = MFX_ERR_NONE;
+
+ bail:
+ return status;
+ }
+private:
+ mfxU8 *m_pAccumulatorPtr;
+ mfxU32 m_nAccumulatorSize;
+ mfxU32 m_nAccumulatorPos;
+ mfxU32 m_nLastRtpTimestamp;
+ MFXVideoDECODE m_Inst;
+ bool m_bInit;
+};
+
+/* ============ H.264 Base/Main Profile X.X Plugin interface functions ================= */
+
+static int tdav_codec_h264_intel_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self;
+ if (param->value_type == tmedia_pvt_int32)
+ {
+ if (tsk_striequals(param->key, "action"))
+ {
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch (action) {
+ case tmedia_codec_action_encode_idr:
+ {
+ h264->encoder.force_idr = tsk_true;
+ return 0;
+ }
+ case tmedia_codec_action_bw_up:
+ case tmedia_codec_action_bw_down:
+ {
+ if (self->opened)
+ {
+ INTEL_CHECK_STATUS(h264->encoder.pInst->UpdateBandwidth(action == tmedia_codec_action_bw_up, TMEDIA_CODEC(h264)->bandwidth_max_upload));
+ }
+ break;
+ }
+ }
+ }
+ else if (tsk_striequals(param->key, "bw_kbps")) // both up and down (from the SDP)
+ {
+ int32_t max_bw_userdefine = tmedia_defaults_get_bandwidth_video_upload_max();
+ int32_t max_bw_new = *((int32_t*)param->value);
+ if (max_bw_userdefine > 0)
+ {
+ // do not use more than what the user defined in it's configuration
+ TMEDIA_CODEC(h264)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine);
+ }
+ else
+ {
+ TMEDIA_CODEC(h264)->bandwidth_max_upload = max_bw_new;
+ }
+ INTEL_DEBUG_INFO("bandwidth-max-upload=%d", TMEDIA_CODEC(h264)->bandwidth_max_upload);
+ if (self->opened)
+ {
+ INTEL_CHECK_STATUS(h264->encoder.pInst->SetMaxBandwidth(TMEDIA_CODEC(h264)->bandwidth_max_upload));
+ }
+ return 0;
+ }
+ else if (tsk_striequals(param->key, "bandwidth-max-upload"))
+ {
+ int32_t bw_max_upload = *((int32_t*)param->value);
+ TSK_DEBUG_INFO("OpenH264 codec: bandwidth-max-upload=%d", bw_max_upload);
+ TMEDIA_CODEC(h264)->bandwidth_max_upload = bw_max_upload;
+ if (self->opened)
+ {
+ INTEL_CHECK_STATUS(h264->encoder.pInst->SetMaxBandwidth(TMEDIA_CODEC(h264)->bandwidth_max_upload));
+ }
+ return 0;
+ }
+ else if (tsk_striequals(param->key, "rotation"))
+ {
+ int rotation = *((int32_t*)param->value);
+ if (h264->encoder.rotation != rotation)
+ {
+ if (self->opened)
+ {
+ int ret;
+ h264->encoder.rotation = rotation;
+ if ((ret = tdav_codec_h264_intel_close_encoder(h264)))
+ {
+ return ret;
+ }
+ if ((ret = tdav_codec_h264_intel_open_encoder(h264)))
+ {
+ return ret;
+ }
+ }
+ }
+ return 0;
+ }
+ }
+bail:
+
+ return -1;
+}
+
+
+static int tdav_codec_h264_intel_open(tmedia_codec_t* self)
+{
+ int ret;
+ tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self;
+
+ if (!h264) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+ // Encoder
+ if ((ret = tdav_codec_h264_intel_open_encoder(h264))) {
+ return ret;
+ }
+
+ // Decoder
+ if ((ret = tdav_codec_h264_intel_open_decoder(h264))) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tdav_codec_h264_intel_close(tmedia_codec_t* self)
+{
+ tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self;
+
+ if (!h264)
+ {
+ INTEL_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) alreasy checked that the codec is opened */
+
+ // Encoder
+ tdav_codec_h264_intel_close_encoder(h264);
+
+ // Decoder
+ tdav_codec_h264_intel_close_decoder(h264);
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_intel_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_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self;
+ (void)(out_data);
+ (void)(out_max_size);
+
+ if (!h264 || !h264->encoder.pInst)
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR);
+ }
+ INTEL_CHECK_STATUS(h264->encoder.pInst->Encode(self, (const mfxU8*)in_data, (mfxU32)in_size));
+bail:
+ return 0;
+}
+
+static tsk_size_t tdav_codec_h264_intel_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_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self;
+
+ if (!h264 || !h264->decoder.pInst)
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR);
+ }
+ return (tsk_size_t)h264->decoder.pInst->Decode(self, (const mfxU8*)in_data, (mfxU32)in_size, out_data, out_max_size, (const trtp_rtp_header_t*)proto_hdr);
+bail:
+ return 0;
+}
+
+static tsk_bool_t tdav_codec_h264_intel_sdp_att_match(const tmedia_codec_t* self, const char* att_name, const char* att_value)
+{
+ return tdav_codec_h264_common_sdp_att_match((tdav_codec_h264_common_t*)self, att_name, att_value);
+}
+
+static char* tdav_codec_h264_intel_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ char* att = tdav_codec_h264_common_sdp_att_get((const tdav_codec_h264_common_t*)self, att_name);
+ if (att && tsk_striequals(att_name, "fmtp")) {
+ tsk_strcat(&att, "; impl=intel");
+ }
+ return att;
+}
+
+/* ============ H.264 Base Profile Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_intel_base_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self;
+ if (h264) {
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ if (tdav_codec_h264_intel_init(h264, profile_idc_baseline) != 0) {
+ return tsk_null;
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_intel_base_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self;
+ if (h264) {
+ /* deinit base */
+ tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
+ /* deinit self */
+ tdav_codec_h264_intel_deinit(h264);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_intel_base_def_s =
+{
+ sizeof(tdav_codec_h264_intel_t),
+ tdav_codec_h264_intel_base_ctor,
+ tdav_codec_h264_intel_base_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_intel_base_plugin_def_s =
+{
+ &tdav_codec_h264_intel_base_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h264_bp,
+ "H264",
+ "H264 Base Profile (Intel Media SDK)",
+ TMEDIA_CODEC_FORMAT_H264_BP,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps) */
+ { 176, 144, 0 }, // fps is @deprecated
+
+ tdav_codec_h264_intel_set,
+ tdav_codec_h264_intel_open,
+ tdav_codec_h264_intel_close,
+ tdav_codec_h264_intel_encode,
+ tdav_codec_h264_intel_decode,
+ tdav_codec_h264_intel_sdp_att_match,
+ tdav_codec_h264_intel_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_intel_base_plugin_def_t = &tdav_codec_h264_intel_base_plugin_def_s;
+
+
+/* ============ H.264 Main Profile Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_h264_intel_main_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self;
+ if (h264) {
+ /* init main: called by tmedia_codec_create() */
+ /* init self */
+ if (tdav_codec_h264_intel_init(h264, profile_idc_main) != 0) {
+ return tsk_null;
+ }
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_h264_intel_main_dtor(tsk_object_t * self)
+{
+ tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self;
+ if (h264)
+ {
+ /* deinit main */
+ tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
+ /* deinit self */
+ tdav_codec_h264_intel_deinit(h264);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_h264_intel_main_def_s =
+{
+ sizeof(tdav_codec_h264_intel_t),
+ tdav_codec_h264_intel_main_ctor,
+ tdav_codec_h264_intel_main_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_h264_intel_main_plugin_def_s =
+{
+ &tdav_codec_h264_intel_main_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_h264_mp,
+ "H264",
+ "H264 main Profile (Intel Media SDK)",
+ TMEDIA_CODEC_FORMAT_H264_MP,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps) */
+ { 176, 144, 0 }, // fps is @deprecated
+
+ tdav_codec_h264_intel_set,
+ tdav_codec_h264_intel_open,
+ tdav_codec_h264_intel_close,
+ tdav_codec_h264_intel_encode,
+ tdav_codec_h264_intel_decode,
+ tdav_codec_h264_intel_sdp_att_match,
+ tdav_codec_h264_intel_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_h264_intel_main_plugin_def_t = &tdav_codec_h264_intel_main_plugin_def_s;
+
+/* ============ Common To all H264 profiles ================= */
+
+static int tdav_codec_h264_intel_open_encoder(tdav_codec_h264_intel_t* self)
+{
+ if (self->encoder.pInst)
+ {
+ INTEL_BREAK("Already initialized");
+ }
+
+ if (!(self->encoder.pInst = new IntelCodecEncoder(self->mfxSession)))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+ INTEL_CHECK_STATUS(self->encoder.pInst->Open(self));
+
+ return 0;
+
+bail:
+ if (self->encoder.pInst)
+ {
+ delete self->encoder.pInst, self->encoder.pInst = NULL;
+ }
+ return -1;
+}
+
+static int tdav_codec_h264_intel_close_encoder(tdav_codec_h264_intel_t* self)
+{
+ if (self)
+ {
+ if (self->encoder.pInst)
+ {
+ delete self->encoder.pInst;
+ self->encoder.pInst = NULL;
+ }
+ self->encoder.frame_count = 0;
+ }
+ return 0;
+}
+
+int tdav_codec_h264_intel_open_decoder(tdav_codec_h264_intel_t* self)
+{
+ if (self->decoder.pInst)
+ {
+ INTEL_BREAK("Already initialized");
+ }
+
+ if (!(self->decoder.pInst = new IntelCodecDecoder(self->mfxSession)))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+ INTEL_CHECK_STATUS(self->decoder.pInst->Open(self));
+
+ return 0;
+
+bail:
+ if (self->decoder.pInst)
+ {
+ delete self->decoder.pInst, self->decoder.pInst = NULL;
+ }
+ return -1;
+}
+
+static int tdav_codec_h264_intel_close_decoder(tdav_codec_h264_intel_t* self)
+{
+ if (self)
+ {
+ if (self->decoder.pInst)
+ {
+ delete self->decoder.pInst;
+ self->decoder.pInst = NULL;
+ }
+ }
+ return 0;
+}
+
+static int tdav_codec_h264_intel_init(tdav_codec_h264_intel_t* self, profile_idc_t profile)
+{
+ int ret = -1;
+ level_idc_t level;
+ tdav_codec_h264_common_t* common = (tdav_codec_h264_common_t*)self;
+#if INTEL_DX11_D3D
+ mfxHDL deviceHandle = NULL;
+#endif
+
+ if (!self) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ goto bail;
+ }
+
+ if ((ret = tdav_codec_h264_common_init(common))) {
+ TSK_DEBUG_ERROR("tdav_codec_h264_intel_common_init() faile with error code=%d", ret);
+ goto bail;
+ }
+
+ if ((ret = tdav_codec_h264_common_level_from_size(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, &level))) {
+ TSK_DEBUG_ERROR("Failed to find level for size=[%u, %u]", TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height);
+ goto bail;
+ }
+
+ if (/*MFUtils::IsLowLatencyH264SupportsMaxSliceSize()*/0) { // TODO: MSDK doesn't support PM=0. Neg. PM=1 but try to do the best to produce SingleNalUnits
+ common->pack_mode_local = H264_PACKETIZATION_MODE;
+ }
+ else {
+ common->pack_mode_local = Non_Interleaved_Mode;
+ }
+ common->profile = profile;
+ common->level = level;
+#if 0
+ // A.2.1.1 Constrained Baseline profile
+ // Conformance of a bitstream to the Constrained Baseline profile is indicated by profile_idc being equal to 66 with
+ // constraint_set1_flag being equal to 1.
+ common->profile_iop = 0xe0; // "constraint_set0_flag=1 and constraint_set1_flag=1" -> Constrained Baseline profile
+#endif
+ TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS * 1000;
+ TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR * 1000;
+
+ TMEDIA_CODEC_VIDEO(self)->in.chroma = tmedia_chroma_yuv420p; // decoded
+ TMEDIA_CODEC_VIDEO(self)->out.chroma = tmedia_chroma_yuv420p; // encoded
+
+ if (!self->mfxSession && !(self->mfxSession = new MFXVideoSession()))
+ {
+ INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC);
+ }
+ INTEL_CHECK_STATUS(self->mfxSession->Init(__IntelDefaultImpl, &__IntelDefaultVer));
+
+#if INTEL_DX11_D3D
+ // Create DirectX device context
+ INTEL_CHECK_STATUS(D3D11_CreateHWDevice(self, *self->mfxSession, &deviceHandle, NULL));
+
+ // Provide device manager to Media SDK
+ INTEL_CHECK_STATUS(self->mfxSession->SetHandle(MFX_HANDLE_D3D11_DEVICE, deviceHandle));
+
+ self->D3D11Allocator.Alloc = D3D11_SimpleAlloc;
+ self->D3D11Allocator.Free = D3D11_SimpleFree;
+ self->D3D11Allocator.Lock = D3D11_SimpleLock;
+ self->D3D11Allocator.Unlock = D3D11_SimpleUnlock;
+ self->D3D11Allocator.GetHDL = D3D11_SimpleGethdl;
+ self->D3D11Allocator.pthis = self;
+
+ // Since we are using video memory we must provide Media SDK with an external allocator
+ INTEL_CHECK_STATUS(self->mfxSession->SetFrameAllocator(&self->D3D11Allocator));
+#endif /* INTEL_DX11_D3D */
+
+ ret = 0;
+
+bail:
+ return ret;
+}
+
+static int tdav_codec_h264_intel_deinit(tdav_codec_h264_intel_t* self)
+{
+ if (!self)
+ {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tdav_codec_h264_intel_close((tmedia_codec_t*)self);
+
+ if (self->mfxSession)
+ {
+ delete self->mfxSession, self->mfxSession = NULL;
+ }
+
+#if INTEL_DX11_D3D
+ D3D11_CleanupHWDevice(self);
+#endif /* INTEL_DX11_D3D */
+
+ return 0;
+}
+
+
+#if INTEL_DX11_D3D
+
+static IDXGIAdapter* D3D11_GetIntelDeviceAdapterHandle(mfxHDL _pthis, mfxSession session)
+{
+ mfxU32 adapterNum = 0;
+ mfxIMPL impl;
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+
+ MFXQueryIMPL(session, &impl);
+
+ mfxIMPL baseImpl = MFX_IMPL_BASETYPE(impl); // Extract Media SDK base implementation type
+
+ // get corresponding adapter number
+ for (mfxU8 i = 0; i < sizeof(implTypes) / sizeof(implTypes[0]); i++)
+ {
+ if (implTypes[i].impl == baseImpl)
+ {
+ adapterNum = implTypes[i].adapterID;
+ break;
+ }
+ }
+
+ HRESULT hres = CreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)(&pthis->pDXGIFactory));
+ if (FAILED(hres))
+ {
+ INTEL_DEBUG_ERROR("CreateDXGIFactory failed: %ld", hres);
+ return NULL;
+ }
+
+ IDXGIAdapter* adapter = NULL;
+ hres = pthis->pDXGIFactory->EnumAdapters(adapterNum, &adapter);
+ if (FAILED(hres))
+ {
+ INTEL_DEBUG_ERROR("EnumAdapters failed: %ld", hres);
+ return NULL;
+ }
+
+ return adapter;
+}
+
+// Create DirectX 11 device context
+// - Required when using D3D surfaces.
+// - D3D Device created and handed to Intel Media SDK
+// - Intel graphics device adapter will be determined automatically (does not have to be primary),
+// but with the following caveats:
+// - Device must be active (but monitor does NOT have to be attached)
+// - Device must be enabled in BIOS. Required for the case when used together with a discrete graphics card
+// - For switchable graphics solutions (mobile) make sure that Intel device is the active device
+static mfxStatus D3D11_CreateHWDevice(mfxHDL _pthis, mfxSession session, mfxHDL* deviceHandle, HWND hWnd)
+{
+ hWnd; // Window handle not required by DX11 since we do not showcase rendering.
+
+ HRESULT hres = S_OK;
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+
+ static D3D_FEATURE_LEVEL FeatureLevels[] = {
+ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0
+ };
+ D3D_FEATURE_LEVEL pFeatureLevelsOut;
+
+ pthis->hAdapter = D3D11_GetIntelDeviceAdapterHandle(_pthis, session);
+ if (NULL == pthis->hAdapter)
+ {
+ INTEL_DEBUG_ERROR("D3D11_GetIntelDeviceAdapterHandle failed");
+ return MFX_ERR_DEVICE_FAILED;
+ }
+
+ hres = D3D11CreateDevice(pthis->hAdapter,
+ D3D_DRIVER_TYPE_UNKNOWN,
+ NULL,
+ 0,
+ FeatureLevels,
+ (sizeof(FeatureLevels) / sizeof(FeatureLevels[0])),
+ D3D11_SDK_VERSION,
+ &pthis->pD3D11Device,
+ &pFeatureLevelsOut,
+ &pthis->pD3D11Ctx);
+ if (FAILED(hres))
+ {
+ INTEL_DEBUG_ERROR("D3D11CreateDevice failed: %ld", hres);
+ return MFX_ERR_DEVICE_FAILED;
+ }
+
+ // turn on multithreading for the DX11 cntext
+ CComQIPtr<ID3D10Multithread> p_mt(pthis->pD3D11Ctx);
+ if (p_mt)
+ {
+ p_mt->SetMultithreadProtected(true);
+ }
+ else
+ {
+ INTEL_DEBUG_ERROR("Failed to create ID3D10Multithread object");
+ return MFX_ERR_DEVICE_FAILED;
+ }
+
+ *deviceHandle = (mfxHDL)pthis->pD3D11Device;
+ return MFX_ERR_NONE;
+}
+
+static void D3D11_CleanupHWDevice(mfxHDL _pthis)
+{
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+ pthis->pD3D11Device = NULL;
+ pthis->pDXGIFactory = NULL;
+ pthis->pD3D11Ctx = NULL;
+ pthis->hAdapter = NULL;
+ pthis->pAdapter = NULL;
+}
+
+static void D3D11_SetHWDeviceContext(mfxHDL _pthis, CComPtr<ID3D11DeviceContext> devCtx)
+{
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+ pthis->pD3D11Ctx = devCtx;
+ devCtx->GetDevice(&pthis->pD3D11Device);
+}
+
+// Intel Media SDK memory allocator entrypoints....
+// - A slightly different allocation procedure is used for encode, decode and VPP
+static mfxStatus _D3D11_SimpleAlloc(mfxHDL _pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response)
+{
+ HRESULT hRes;
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+
+ // Determine surface format (current simple implementation only supports NV12 and RGB4(32))
+ DXGI_FORMAT format;
+ if (MFX_FOURCC_NV12 == request->Info.FourCC)
+ {
+ format = DXGI_FORMAT_NV12;
+ }
+ else if (MFX_FOURCC_RGB4 == request->Info.FourCC)
+ {
+ format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ }
+ else
+ {
+ format = DXGI_FORMAT_P8;
+ }
+
+ // Allocate custom container to keep texture and stage buffers for each surface
+ // Container also stores the intended read and/or write operation.
+ CustomMemId** mids = new CustomMemId *[request->NumFrameSuggested];
+ if (!mids) return MFX_ERR_MEMORY_ALLOC;
+ for (int i = 0; i < request->NumFrameSuggested; i++)
+ {
+ mids[i] = new CustomMemId;
+ memset(mids[i], 0, sizeof(CustomMemId));
+ mids[i]->rw = request->Type & 0xF000; // Set intended read/write operation
+ }
+
+ request->Type = request->Type & 0x0FFF;
+
+ // because P8 data (bitstream) for h264 encoder should be allocated by CreateBuffer()
+ // but P8 data (MBData) for MPEG2 encoder should be allocated by CreateTexture2D()
+ if (request->Info.FourCC == MFX_FOURCC_P8)
+ {
+ D3D11_BUFFER_DESC desc = { 0 };
+
+ desc.ByteWidth = request->Info.Width * request->Info.Height;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+
+ ID3D11Buffer * buffer = 0;
+ hRes = pthis->pD3D11Device->CreateBuffer(&desc, 0, &buffer);
+ if (FAILED(hRes))
+ {
+ INTEL_DEBUG_ERROR("CreateBuffer failed:%ld", hRes);
+ return MFX_ERR_MEMORY_ALLOC;
+ }
+
+ mids[0]->memId = reinterpret_cast<ID3D11Texture2D *>(buffer);
+ }
+ else
+ {
+ D3D11_TEXTURE2D_DESC desc = { 0 };
+
+ desc.Width = request->Info.Width;
+ desc.Height = request->Info.Height;
+ desc.MipLevels = 1;
+ desc.ArraySize = 1; // number of subresources is 1 in this case
+ desc.Format = format;
+ desc.SampleDesc.Count = 1;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_DECODER;
+ desc.MiscFlags = 0;
+
+ if ((MFX_MEMTYPE_FROM_VPPIN & request->Type) &&
+ (DXGI_FORMAT_B8G8R8A8_UNORM == desc.Format))
+ {
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET;
+ if (desc.ArraySize > 2)
+ return MFX_ERR_MEMORY_ALLOC;
+ }
+
+ if ((MFX_MEMTYPE_FROM_VPPOUT & request->Type) ||
+ (MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET & request->Type))
+ {
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET;
+ if (desc.ArraySize > 2)
+ {
+ return MFX_ERR_MEMORY_ALLOC;
+ }
+ }
+
+ if (DXGI_FORMAT_P8 == desc.Format)
+ desc.BindFlags = 0;
+
+ ID3D11Texture2D* pTexture2D;
+
+ // Create surface textures
+ for (size_t i = 0; i < request->NumFrameSuggested / desc.ArraySize; i++)
+ {
+ hRes = pthis->pD3D11Device->CreateTexture2D(&desc, NULL, &pTexture2D);
+
+ if (FAILED(hRes))
+ {
+ INTEL_DEBUG_ERROR("CreateTexture2D failed:%ld", hRes);
+ return MFX_ERR_MEMORY_ALLOC;
+ }
+
+ mids[i]->memId = pTexture2D;
+ }
+
+ desc.ArraySize = 1;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ /*| D3D11_CPU_ACCESS_WRITE*/;
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+
+ // Create surface staging textures
+ for (size_t i = 0; i < request->NumFrameSuggested; i++)
+ {
+ hRes = pthis->pD3D11Device->CreateTexture2D(&desc, NULL, &pTexture2D);
+
+ if (FAILED(hRes))
+ {
+ INTEL_DEBUG_ERROR("CreateTexture2D failed:%ld", hRes);
+ return MFX_ERR_MEMORY_ALLOC;
+ }
+
+ mids[i]->memIdStage = pTexture2D;
+ }
+ }
+
+ response->mids = (mfxMemId*)mids;
+ response->NumFrameActual = request->NumFrameSuggested;
+
+ return MFX_ERR_NONE;
+}
+
+static mfxStatus D3D11_SimpleAlloc(mfxHDL _pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response)
+{
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+ mfxStatus sts = MFX_ERR_NONE;
+ int idx = (MFX_MEMTYPE_FROM_DECODE & request->Type) ? 1 : 0;
+
+ if (request->NumFrameSuggested <= pthis->D3D11SavedAllocResponses[idx].NumFrameActual &&
+ MFX_MEMTYPE_EXTERNAL_FRAME & request->Type &&
+ MFX_MEMTYPE_FROM_DECODE & request->Type &&
+ pthis->D3D11SavedAllocResponses[idx].NumFrameActual != 0)
+ {
+ // Memory for this request was already allocated during manual allocation stage. Return saved response
+ // When decode acceleration device (DXVA) is created it requires a list of d3d surfaces to be passed.
+ // Therefore Media SDK will ask for the surface info/mids again at Init() stage, thus requiring us to return the saved response
+ // (No such restriction applies to Encode or VPP)
+
+ *response = pthis->D3D11SavedAllocResponses[idx];
+ }
+ else
+ {
+ sts = _D3D11_SimpleAlloc(_pthis, request, response);
+ pthis->D3D11SavedAllocResponses[idx] = *response;
+ }
+
+ return sts;
+}
+
+static mfxStatus D3D11_SimpleLock(mfxHDL _pthis, mfxMemId mid, mfxFrameData *ptr)
+{
+ HRESULT hRes = S_OK;
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+
+ D3D11_TEXTURE2D_DESC desc = { 0 };
+ D3D11_MAPPED_SUBRESOURCE lockedRect = { 0 };
+
+ CustomMemId* memId = (CustomMemId*)mid;
+ ID3D11Texture2D* pSurface = (ID3D11Texture2D *)memId->memId;
+ ID3D11Texture2D* pStage = (ID3D11Texture2D *)memId->memIdStage;
+
+ D3D11_MAP mapType = D3D11_MAP_READ;
+ UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT;
+
+ if (NULL == pStage)
+ {
+ hRes = pthis->pD3D11Ctx->Map(pSurface, 0, mapType, mapFlags, &lockedRect);
+ desc.Format = DXGI_FORMAT_P8;
+ }
+ else
+ {
+ pSurface->GetDesc(&desc);
+
+ // copy data only in case of user wants o read from stored surface
+ if (memId->rw & D3D11_WILL_READ)
+ {
+ pthis->pD3D11Ctx->CopySubresourceRegion(pStage, 0, 0, 0, 0, pSurface, 0, NULL);
+ }
+
+ do
+ {
+ hRes = pthis->pD3D11Ctx->Map(pStage, 0, mapType, mapFlags, &lockedRect);
+ if (S_OK != hRes && DXGI_ERROR_WAS_STILL_DRAWING != hRes)
+ {
+ return MFX_ERR_LOCK_MEMORY;
+ }
+ } while (DXGI_ERROR_WAS_STILL_DRAWING == hRes);
+ }
+
+ if (FAILED(hRes))
+ {
+ return MFX_ERR_LOCK_MEMORY;
+ }
+
+ switch (desc.Format)
+ {
+ case DXGI_FORMAT_NV12:
+ ptr->Pitch = (mfxU16)lockedRect.RowPitch;
+ ptr->Y = (mfxU8 *)lockedRect.pData;
+ ptr->U = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch;
+ ptr->V = ptr->U + 1;
+ break;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ ptr->Pitch = (mfxU16)lockedRect.RowPitch;
+ ptr->B = (mfxU8 *)lockedRect.pData;
+ ptr->G = ptr->B + 1;
+ ptr->R = ptr->B + 2;
+ ptr->A = ptr->B + 3;
+ break;
+ case DXGI_FORMAT_P8:
+ ptr->Pitch = (mfxU16)lockedRect.RowPitch;
+ ptr->Y = (mfxU8 *)lockedRect.pData;
+ ptr->U = 0;
+ ptr->V = 0;
+ break;
+ default:
+ return MFX_ERR_LOCK_MEMORY;
+ }
+
+ return MFX_ERR_NONE;
+}
+
+static mfxStatus D3D11_SimpleUnlock(mfxHDL _pthis, mfxMemId mid, mfxFrameData *ptr)
+{
+ tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis;
+
+ CustomMemId* memId = (CustomMemId*)mid;
+ ID3D11Texture2D* pSurface = (ID3D11Texture2D *)memId->memId;
+ ID3D11Texture2D* pStage = (ID3D11Texture2D *)memId->memIdStage;
+
+ if (NULL == pStage)
+ {
+ pthis->pD3D11Ctx->Unmap(pSurface, 0);
+ }
+ else
+ {
+ pthis->pD3D11Ctx->Unmap(pStage, 0);
+ // copy data only in case of user wants to write to stored surface
+ if (memId->rw & D3D11_WILL_WRITE)
+ {
+ pthis->pD3D11Ctx->CopySubresourceRegion(pSurface, 0, 0, 0, 0, pStage, 0, NULL);
+ }
+ }
+
+ if (ptr)
+ {
+ ptr->Pitch = 0;
+ ptr->U = ptr->V = ptr->Y = 0;
+ ptr->A = ptr->R = ptr->G = ptr->B = 0;
+ }
+
+ return MFX_ERR_NONE;
+}
+
+static mfxStatus D3D11_SimpleGethdl(mfxHDL _pthis, mfxMemId mid, mfxHDL *handle)
+{
+ _pthis;
+ if (NULL == handle)
+ {
+ return MFX_ERR_INVALID_HANDLE;
+ }
+
+ mfxHDLPair* pPair = (mfxHDLPair*)handle;
+ CustomMemId* memId = (CustomMemId*)mid;
+
+ pPair->first = memId->memId; // surface texture
+ pPair->second = 0;
+
+ return MFX_ERR_NONE;
+}
+
+static mfxStatus D3D11_SimpleFree(mfxHDL _pthis, mfxFrameAllocResponse *response)
+{
+ _pthis;
+ if (NULL == response)
+ {
+ return MFX_ERR_NULL_PTR;
+ }
+
+ if (response->mids)
+ {
+ for (mfxU32 i = 0; i < response->NumFrameActual; i++)
+ {
+ if (response->mids[i])
+ {
+ CustomMemId* mid = (CustomMemId*)response->mids[i];
+ ID3D11Texture2D* pSurface = (ID3D11Texture2D *)mid->memId;
+ ID3D11Texture2D* pStage = (ID3D11Texture2D *)mid->memIdStage;
+ if (pSurface)
+ {
+ pSurface->Release();
+ }
+ if (pStage)
+ {
+ pStage->Release();
+ }
+ delete mid;
+ }
+ }
+ }
+
+ delete[] response->mids;
+ response->mids = 0;
+
+ return MFX_ERR_NONE;
+}
+
+#endif /* INTEL_DX11_D3D */
+
+#endif /* HAVE_INTEL_MEDIA_SDK */
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c b/tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c
new file mode 100644
index 0000000..5742f43
--- /dev/null
+++ b/tinyDAV/src/codecs/h264/tdav_codec_h264_rtp.c
@@ -0,0 +1,411 @@
+/*
+* 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_h264_rtp.c
+ * @brief H.264 payloader/depayloder as per RFC 3984
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ */
+#include "tinydav/codecs/h264/tdav_codec_h264_rtp.h"
+
+#include "tinydav/codecs/h264/tdav_codec_h264_common.h"
+
+#include "tinymedia/tmedia_codec.h"
+
+#include "tsk_string.h"
+#include "tsk_debug.h"
+
+#include "tsk_memory.h"
+#include <string.h> /* strlen() */
+#include <stdlib.h> /* strtol() */
+
+/*
+* ITU H.264 - http://www.itu.int/rec/T-REC-H.264-200903-S/en
+*/
+
+uint8_t H264_START_CODE_PREFIX[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+#define H264_NAL_UNIT_TYPE_HEADER_SIZE 1
+#define H264_F_UNIT_TYPE_HEADER_SIZE 1
+#define H264_FUA_HEADER_SIZE 2
+#define H264_FUB_HEADER_SIZE 4
+#define H264_NAL_AGG_MAX_SIZE 65535
+
+static int tdav_codec_h264_get_fua_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp, tsk_bool_t* end_of_unit);
+static int tdav_codec_h264_get_nalunit_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size);
+
+// profile_level_id MUST be a "null-terminated" string
+int tdav_codec_h264_parse_profile(const char* profile_level_id, profile_idc_t *p_idc, profile_iop_t *p_iop, level_idc_t *l_idc)
+{
+ uint32_t value;
+
+ if(tsk_strlen(profile_level_id) != 6){
+ TSK_DEBUG_ERROR("I say [%s] is an invalid profile-level-id", profile_level_id);
+ return -1;
+ }
+
+ value = strtol(profile_level_id, tsk_null, 16);
+
+ /* profile-idc */
+ if(p_idc){
+ switch((value >> 16)){
+ case profile_idc_baseline:
+ *p_idc = profile_idc_baseline;
+ break;
+ case profile_idc_extended:
+ *p_idc = profile_idc_extended;
+ break;
+ case profile_idc_main:
+ *p_idc = profile_idc_main;
+ break;
+ case profile_idc_high:
+ *p_idc = profile_idc_high;
+ break;
+ default:
+ *p_idc = profile_idc_none;
+ break;
+ }
+ }
+
+ /* profile-iop */
+ if(p_iop){
+ p_iop->constraint_set0_flag = ((value >> 8) & 0x80)>>7;
+ p_iop->constraint_set1_flag = ((value >> 8) & 0x40)>>6;
+ p_iop->constraint_set2_flag = ((value >> 8) & 0x20)>>5;
+ p_iop->reserved_zero_5bits = ((value >> 8) & 0x1F);
+ }
+
+ /* level-idc */
+ if(l_idc){
+ switch((value & 0xFF)){
+ case level_idc_1_0:
+ *l_idc = level_idc_1_0;
+ break;
+ case level_idc_1_b:
+ *l_idc = level_idc_1_b;
+ break;
+ case level_idc_1_1:
+ *l_idc = level_idc_1_1;
+ break;
+ case level_idc_1_2:
+ *l_idc = level_idc_1_2;
+ break;
+ case level_idc_1_3:
+ *l_idc = level_idc_1_3;
+ break;
+ case level_idc_2_0:
+ *l_idc = level_idc_2_0;
+ break;
+ case level_idc_2_1:
+ *l_idc = level_idc_2_1;
+ break;
+ case level_idc_2_2:
+ *l_idc = level_idc_2_2;
+ break;
+ case level_idc_3_0:
+ *l_idc = level_idc_3_0;
+ break;
+ case level_idc_3_1:
+ *l_idc = level_idc_3_1;
+ break;
+ case level_idc_3_2:
+ *l_idc = level_idc_3_2;
+ break;
+ case level_idc_4_0:
+ *l_idc = level_idc_4_0;
+ break;
+ case level_idc_4_1:
+ *l_idc = level_idc_4_1;
+ break;
+ case level_idc_4_2:
+ *l_idc = level_idc_4_2;
+ break;
+ case level_idc_5_0:
+ *l_idc = level_idc_5_0;
+ break;
+ case level_idc_5_1:
+ *l_idc = level_idc_5_1;
+ break;
+ case level_idc_5_2:
+ *l_idc = level_idc_5_2;
+ break;
+ default:
+ *l_idc = level_idc_none;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int tdav_codec_h264_get_pay(const void* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp, tsk_bool_t* end_of_unit)
+{
+ const uint8_t* pdata = (const uint8_t*)in_data;
+ uint8_t nal_type;
+ if (!in_data || !in_size || !out_data || !out_size || !append_scp || !end_of_unit) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ *out_data = tsk_null;
+ *out_size = 0;
+
+ /* 5.3. NAL Unit Octet Usage
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ */
+ switch ((nal_type = (pdata[0] & 0x1F))) {
+ case undefined_0:
+ case undefined_30:
+ case undefined_31:
+ case stap_a:
+ case stap_b:
+ case mtap16:
+ case mtap24:
+ case fu_b:
+ break;
+ case fu_a:
+ return tdav_codec_h264_get_fua_pay(pdata, in_size, out_data, out_size, append_scp, end_of_unit);
+ default: /* NAL unit (1-23) */
+ *append_scp = tsk_true; //(nal_type != 7 && nal_type != 8); // SPS or PPS
+ *end_of_unit = tsk_true;
+ return tdav_codec_h264_get_nalunit_pay(pdata, in_size, out_data, out_size);
+ }
+
+ TSK_DEBUG_WARN("%d not supported as valid NAL Unit type", (*pdata & 0x1F));
+ return -1;
+}
+
+
+static int tdav_codec_h264_get_fua_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size, tsk_bool_t* append_scp, tsk_bool_t* end_of_unit)
+{
+ if (in_size <=H264_FUA_HEADER_SIZE) {
+ TSK_DEBUG_ERROR("Too short");
+ return -1;
+ }
+ /* RFC 3984 - 5.8. Fragmentation Units (FUs)
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | FU indicator | FU header | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ | |
+ | FU payload |
+ | |
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :...OPTIONAL RTP padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ The FU indicator octet has the following format:
+
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+
+ The FU header has the following format:
+
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |S|E|R| Type |
+ +---------------+
+ */
+
+ if (((in_data[1] & 0x80) /*S*/)) {
+ /* discard "FU indicator" */
+ *out_data = (in_data + H264_NAL_UNIT_TYPE_HEADER_SIZE);
+ *out_size = (in_size - H264_NAL_UNIT_TYPE_HEADER_SIZE);
+
+ // Do need to append Start Code Prefix ?
+ /* S: 1 bit
+ When set to one, the Start bit indicates the start of a fragmented
+ NAL unit. When the following FU payload is not the start of a
+ fragmented NAL unit payload, the Start bit is set to zero.*/
+ *append_scp = tsk_true;
+
+ // F, NRI and Type
+ *((uint8_t*)*out_data) = (in_data[0] & 0xe0) /* F,NRI from "FU indicator"*/ | (in_data[1] & 0x1f) /* type from "FU header" */;
+ }
+ else {
+ *append_scp = tsk_false;
+ *out_data = (in_data + H264_FUA_HEADER_SIZE);
+ *out_size = (in_size - H264_FUA_HEADER_SIZE);
+ }
+ /*
+ E: 1 bit
+ When set to one, the End bit indicates the end of a fragmented
+ NAL unit, i.e., the last byte of the payload is also the last
+ byte of the fragmented NAL unit. When the following FU
+ payload is not the last fragment of a fragmented NAL unit, the
+ End bit is set to zero.
+ */
+ *end_of_unit = (((in_data[1] & 0x40) /*E*/)) ? tsk_true : tsk_false;
+
+ return 0;
+}
+
+static int tdav_codec_h264_get_nalunit_pay(const uint8_t* in_data, tsk_size_t in_size, const void** out_data, tsk_size_t *out_size)
+{
+
+/* 5.6. Single NAL Unit Packet
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |F|NRI| type | |
+ +-+-+-+-+-+-+-+-+ |
+ | |
+ | Bytes 2..n of a Single NAL unit |
+ | |
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :...OPTIONAL RTP padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+ *out_data = in_data;
+ *out_size = in_size;
+
+ return 0;
+}
+
+void tdav_codec_h264_rtp_encap(struct tdav_codec_h264_common_s* self, const uint8_t* pdata, tsk_size_t size)
+{
+ static const tsk_size_t size_of_scp = sizeof(H264_START_CODE_PREFIX); /* we know it's equal to 4 .. */
+ register tsk_size_t i;
+ tsk_size_t last_scp, prev_scp;
+ tsk_size_t _size;
+
+ if (!pdata || size < size_of_scp) {
+ return;
+ }
+
+ if (pdata[0] == 0 && pdata[1] == 0) {
+ if (pdata[2] == 1) {
+ pdata += 3, size -= 3;
+ }
+ else if (pdata[2] == 0 && pdata[3] == 1) {
+ pdata += 4, size -= 4;
+ }
+ }
+
+ _size = (size - size_of_scp);
+ last_scp = 0, prev_scp = 0;
+ for (i = size_of_scp; i<_size; i++) {
+ if (pdata[i] == 0 && pdata[i+1] == 0 && (pdata[i+2] == 1 || (pdata[i+2] == 0 && pdata[i+3] == 1))) { /* Find Start Code Prefix */
+ prev_scp = last_scp;
+ if ((i - last_scp) >= H264_RTP_PAYLOAD_SIZE || 1) {
+ tdav_codec_h264_rtp_callback(self, pdata + prev_scp,
+ (i - prev_scp), (prev_scp == size));
+ }
+ last_scp = i;
+ i += (pdata[i+2] == 1) ? 3 : 4;
+ }
+ }
+
+ if (last_scp < (int32_t)size) {
+ tdav_codec_h264_rtp_callback(self, pdata + last_scp,
+ (size - last_scp), tsk_true);
+ }
+}
+
+void tdav_codec_h264_rtp_callback(struct tdav_codec_h264_common_s *self, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+ uint8_t* pdata = (uint8_t*)data;
+
+ //TSK_DEBUG_INFO("%x %x %x %x -- %u", pdata[0], pdata[1], pdata[2], pdata[3], size);
+
+ if (size>4 && pdata[0] == H264_START_CODE_PREFIX[0] && pdata[1] == H264_START_CODE_PREFIX[1]) {
+ if(pdata[2] == H264_START_CODE_PREFIX[3]){
+ pdata += 3, size -= 3;
+ }
+ else if (pdata[2] == H264_START_CODE_PREFIX[2] && pdata[3] == H264_START_CODE_PREFIX[3]) {
+ pdata += 4, size -= 4;
+ }
+ }
+
+ //TSK_DEBUG_INFO("==> SCP %2x %2x %2x %2x", pdata[0], pdata[1], pdata[2], pdata[3]);
+
+ if (self->pack_mode_local == Single_NAL_Unit_Mode || size < H264_RTP_PAYLOAD_SIZE) {
+ if (self->pack_mode_local == Single_NAL_Unit_Mode && size > H264_RTP_PAYLOAD_SIZE) {
+ TSK_DEBUG_WARN("pack_mode=Single_NAL_Unit_Mode but size(%d) > H264_RTP_PAYLOAD_SIZE(%d). Did you forget to set \"avctx->rtp_payload_size\"?", size, H264_RTP_PAYLOAD_SIZE);
+ }
+ // Can be packet in a Single Nal Unit
+ // Send data over the network
+ if (TMEDIA_CODEC_VIDEO(self)->out.callback) {
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = pdata;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = size;
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1./(double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = marker;
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
+ }
+ else if (size > H264_NAL_UNIT_TYPE_HEADER_SIZE) {
+ /* Should be Fragmented as FUA */
+ uint8_t fua_hdr[H264_FUA_HEADER_SIZE]; /* "FU indicator" and "FU header" - 2bytes */
+ fua_hdr[0] = pdata[0] & 0x60/* NRI */, fua_hdr[0] |= fu_a;
+ fua_hdr[1] = 0x80/* S=1,E=0,R=0 */, fua_hdr[1] |= pdata[0] & 0x1f; /* type */
+ // discard header
+ pdata += H264_NAL_UNIT_TYPE_HEADER_SIZE;
+ size -= H264_NAL_UNIT_TYPE_HEADER_SIZE;
+
+ while(size) {
+ tsk_size_t packet_size = TSK_MIN(H264_RTP_PAYLOAD_SIZE, size);
+
+ if (self->rtp.size < (packet_size + H264_FUA_HEADER_SIZE)){
+ if(!(self->rtp.ptr = (uint8_t*)tsk_realloc(self->rtp.ptr, (packet_size + H264_FUA_HEADER_SIZE)))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return;
+ }
+ self->rtp.size = (packet_size + H264_FUA_HEADER_SIZE);
+ }
+ // set E bit
+ if((size - packet_size) == 0){
+ // Last packet
+ fua_hdr[1] |= 0x40;
+ }
+ // copy FUA header
+ memcpy(self->rtp.ptr, fua_hdr, H264_FUA_HEADER_SIZE);
+ // reset "S" bit
+ fua_hdr[1] &= 0x7F;
+ // copy data
+ memcpy((self->rtp.ptr + H264_FUA_HEADER_SIZE), pdata, packet_size);
+ pdata += packet_size;
+ size -= packet_size;
+
+ // send data
+ if(TMEDIA_CODEC_VIDEO(self)->out.callback){
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->rtp.ptr;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (packet_size + H264_FUA_HEADER_SIZE);
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1./(double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = (size == 0);
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
+ }
+ }
+}
diff --git a/tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c b/tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c
new file mode 100644
index 0000000..65df6ad
--- /dev/null
+++ b/tinyDAV/src/codecs/ilbc/tdav_codec_ilbc.c
@@ -0,0 +1,265 @@
+/*
+* 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_ilbc.c
+ * @brief iLBC codec
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/ilbc/tdav_codec_ilbc.h"
+
+#if HAVE_ILBC
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TDAV_ILBC_MODE 20
+
+/* ============ iLBC Plugin interface ================= */
+
+#define tdav_codec_ilbc_set tsk_null
+
+static int tdav_codec_ilbc_open(tmedia_codec_t* self)
+{
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+
+ initEncode(&ilbc->encoder, TDAV_ILBC_MODE);
+ initDecode(&ilbc->decoder, TDAV_ILBC_MODE, tsk_true/* Enhancer */);
+
+ return 0;
+}
+
+static int tdav_codec_ilbc_close(tmedia_codec_t* self)
+{
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+
+ //ilbc->encoder = {0};
+ //ilbc->decoder = {0};
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_ilbc_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_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+ int k;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* convert signal to float */
+ for(k=0; k<ilbc->encoder.blockl; k++){
+ ilbc->encblock[k] = (float)((short*)in_data)[k];
+ }
+
+ /* allocate new buffer if needed */
+ if((int)*out_max_size <ilbc->encoder.no_of_bytes){
+ if(!(*out_data = tsk_realloc(*out_data, ilbc->encoder.no_of_bytes))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = ilbc->encoder.no_of_bytes;
+ }
+
+ /* do the actual encoding */
+ iLBC_encode(*out_data, ilbc->encblock, &ilbc->encoder);
+
+ return ilbc->encoder.no_of_bytes;
+}
+
+static tsk_size_t tdav_codec_ilbc_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)
+{
+ int blocks, i, k, block_size;
+ float dtmp;
+ tsk_size_t out_size;
+ tdav_codec_ilbc_t* ilbc = (tdav_codec_ilbc_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if((in_size % NO_OF_BYTES_20MS) == 0){
+ /* Using 20ms mode */
+ blocks = (in_size/NO_OF_BYTES_20MS);
+ out_size = (BLOCKL_20MS * blocks) * sizeof(short);
+ block_size = out_size/blocks;
+ if(ilbc->decoder.mode != 20){
+ initDecode(&ilbc->decoder, 20, tsk_true/* Enhancer */);
+ }
+ }
+ else if((in_size % NO_OF_BYTES_30MS) == 0){
+ /* Using 30ms mode */
+ blocks = (in_size/NO_OF_BYTES_30MS);
+ out_size = (BLOCKL_30MS * blocks) * sizeof(short);
+ block_size = out_size/blocks;
+ if(ilbc->decoder.mode != 30){
+ initDecode(&ilbc->decoder, 30, tsk_true/* Enhancer */);
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid iLBC mode");
+ return 0;
+ }
+
+ /* allocate new buffer if needed */
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+
+ for(i = 0; i<blocks; i++){
+ iLBC_decode(ilbc->decblock, &((uint8_t*)in_data)[i*block_size], &ilbc->decoder, 1/* Normal */);
+
+ /* convert to short */
+ for(k=0; k<ilbc->decoder.blockl; k++){
+ dtmp=ilbc->decblock[k];
+
+ if(dtmp<MIN_SAMPLE){
+ dtmp = MIN_SAMPLE;
+ }
+ else if(dtmp>MAX_SAMPLE){
+ dtmp = MAX_SAMPLE;
+ }
+
+ ((short*)*out_data)[(i*block_size) + k] = ((short) dtmp);
+ }
+ }
+
+ return out_size;
+}
+
+static char* tdav_codec_ilbc_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ if(tsk_striequals(att_name, "fmtp")){
+ char* fmtp = tsk_null;
+ tsk_sprintf(&fmtp, "mode=%d", TDAV_ILBC_MODE);
+ return fmtp;
+ }
+ return tsk_null;
+}
+
+static tsk_bool_t tdav_codec_ilbc_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ if(tsk_striequals(att_name, "fmtp")){
+ /* RFC 3952 - 5. Mapping To SDP Parameters
+
+ The offer contains the preferred mode of the offerer. The answerer
+ may agree to that mode by including the same mode in the answer, or
+ may include a different mode. The resulting mode used by both
+ parties SHALL be the lower of the bandwidth modes in the offer and
+ answer.
+
+ That is, an offer of "mode=20" receiving an answer of "mode=30" will
+ result in "mode=30" being used by both participants. Similarly, an
+ offer of "mode=30" and an answer of "mode=20" will result in
+ "mode=30" being used by both participants.
+
+ This is important when one end point utilizes a bandwidth constrained
+ link (e.g., 28.8k modem link or slower), where only the lower frame
+ size will work.
+ */
+ return tsk_true; // FIXME
+ }
+ return tsk_true;
+}
+
+
+//
+// iLBC Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_ilbc_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_ilbc_t *ilbc = self;
+ if(ilbc){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_ilbc_dtor(tsk_object_t * self)
+{
+ tdav_codec_ilbc_t *ilbc = self;
+ if(ilbc){
+ /* deinit base */
+ tmedia_codec_audio_deinit(ilbc);
+ /* deinit self */
+ //ilbc->encoder = {0};
+ //ilbc->decoder = {0};
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_ilbc_def_s =
+{
+ sizeof(tdav_codec_ilbc_t),
+ tdav_codec_ilbc_ctor,
+ tdav_codec_ilbc_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_ilbc_plugin_def_s =
+{
+ &tdav_codec_ilbc_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_ilbc,
+ "iLBC",
+ "iLBC codec (libILBc)",
+ TMEDIA_CODEC_FORMAT_ILBC,
+ tsk_true,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tdav_codec_ilbc_set,
+ tdav_codec_ilbc_open,
+ tdav_codec_ilbc_close,
+ tdav_codec_ilbc_encode,
+ tdav_codec_ilbc_decode,
+ tdav_codec_ilbc_sdp_att_match,
+ tdav_codec_ilbc_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_ilbc_plugin_def_t = &tdav_codec_ilbc_plugin_def_s;
+
+
+#endif /* HAVE_ILBC */
diff --git a/tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c b/tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c
new file mode 100644
index 0000000..e48cb00
--- /dev/null
+++ b/tinyDAV/src/codecs/mp4ves/tdav_codec_mp4ves.c
@@ -0,0 +1,818 @@
+/*
+* 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_mp4ves.c
+ * @brief MP4V-ES codec plugin
+ * RTP payloader/depayloader follows RFC 3016.
+ * ISO-IEC-14496-2: http://www.csus.edu/indiv/p/pangj/aresearch/video_compression/presentation/ISO-IEC-14496-2_2001_MPEG4_Visual.pdf
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Th Dec 2 16:54:58 2010 mdiop
+ */
+#include "tinydav/codecs/mp4ves/tdav_codec_mp4ves.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tnet_endianness.h"
+
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_string.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <libavcodec/avcodec.h>
+
+#define DEFAULT_PROFILE_LEVEL_ID Simple_Profile_Level_1
+
+#define MP4V_GOP_SIZE_IN_SECONDS 25
+#define MP4V_RTP_PAYLOAD_SIZE 900
+
+typedef struct tdav_codec_mp4ves_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ int profile;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+ tsk_bool_t force_idr;
+ int quality; // [1-31]
+ int rotation;
+ int32_t max_bw_kpbs;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_mp4ves_t;
+
+// From ISO-IEC-14496-2
+typedef enum mp4v_codes_e
+{
+ // To initiate a visual session (6.3.2)
+ visual_object_sequence_start_code = 0x000001B0,
+ // To terminate a visual session (6.3.2)
+ visual_object_sequence_end_code = 0x000001B1,
+ // To initiate a visual object (6.3.2)
+ visual_object_start_code = 0x000001B5,
+ // To identify the beginning of user data. The user data continues until receipt of another start code. (6.3.2.1)
+ user_data_start_code = 0x000001B2,
+ // The video_object_layer_start_code is a string of 32 bits. The first 28 bits are
+ // ‘0000 0000 0000 0000 0000 0001 0010‘ in binary and the last 4-bits represent one of the values in the range of
+ // ‘0000’ to ‘1111’ in binary. The video_object_layer_start_code marks a new video object layer. (6.3.3)
+ video_object_layer_start_code = 0x0000012,
+ // To identify the beginning of a GOV header (6.3.4)
+ group_of_vop_start_code = 0x000001B3,
+ // To mark the start of a video object plane (6.3.5 )
+ vop_start_code = 0x000001B6,
+}
+mp4v_start_code_t;
+
+// From ISO-IEC-14496-2 Annex G
+typedef enum mp4v_profiles_e
+{
+ /* Reserved = 0x00000000 */
+ Simple_Profile_Level_1 = 1,
+ Simple_Profile_Level_2 = 2,
+ Simple_Profile_Level_3 = 3,
+ /* Reserved 00000100 ? 00010000 */
+ Simple_Scalable_Profile_Level_1 = 17,
+ Simple_Scalable_Profile_Level_2 = 18,
+ /* Reserved 00010011 ? = 0x00100000 */
+ Core_Profile_Level_1 = 33,
+ Core_Profile_Level_2 = 34,
+ /* Reserved 00100011 ? = 0x00110001 */
+ Main_Profile_Level_2 = 50,
+ Main_Profile_Level_3 = 51,
+ Main_Profile_Level_4 = 52,
+ /* Reserved 00110101 ? = 0x01000001 */
+ N_bit_Profile_Level_2 = 66,
+ /* Reserved 01000011 ? = 0x01010000 */
+ Scalable_Texture_Profile_Level_1 = 81,
+ /* Reserved 01010010 ? 01100000 */
+ Simple_Face_Animation_Profile_Level_1 = 97,
+ Simple_Face_Animation_Profile_Level_2 = 98,
+ Simple_FBA_Profile_Level_1 = 99,
+ Simple_FBA_Profile_Level_2 = 100,
+ /* Reserved 01100101 ? 01110000 */
+ Basic_Animated_Texture_Profile_Level_1 = 113,
+ Basic_Animated_Texture_Profile_Level_2 = 114,
+ /* Reserved 01110011 ? 10000000 */
+ Hybrid_Profile_Level_1 = 129,
+ Hybrid_Profile_Level_2 = 130,
+ /* Reserved 10000011 ? 10010000 */
+ Advanced_Real_Time_Simple_Profile_Level_1 = 145,
+ Advanced_Real_Time_Simple_Profile_Level_2 = 146,
+ Advanced_Real_Time_Simple_Profile_Level_3 = 147,
+ Advanced_Real_Time_Simple_Profile_Level_4 = 148,
+ /* Reserved 10010101 ? 10100000 */
+}
+mp4v_profiles_t;
+
+static int tdav_codec_mp4ves_open_encoder(tdav_codec_mp4ves_t* self);
+static int tdav_codec_mp4ves_open_decoder(tdav_codec_mp4ves_t* self);
+static int tdav_codec_mp4ves_close_encoder(tdav_codec_mp4ves_t* self);
+static int tdav_codec_mp4ves_close_decoder(tdav_codec_mp4ves_t* self);
+
+static void tdav_codec_mp4ves_encap(tdav_codec_mp4ves_t* mp4v, const uint8_t* pdata, tsk_size_t size);
+static void tdav_codec_mp4ves_rtp_callback(tdav_codec_mp4ves_t *mp4v, const void *data, tsk_size_t size, tsk_bool_t marker);
+
+/* ============ MP4V-ES Plugin interface functions ================= */
+
+static int tdav_codec_mp4ves_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_mp4ves_t* mp4ves = (tdav_codec_mp4ves_t*)self;
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Codec not opened");
+ return -1;
+ }
+ if(param->value_type == tmedia_pvt_int32){
+ if(tsk_striequals(param->key, "action")){
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch(action){
+ case tmedia_codec_action_encode_idr:
+ {
+ mp4ves->encoder.force_idr = tsk_true;
+ break;
+ }
+ case tmedia_codec_action_bw_down:
+ {
+ mp4ves->encoder.quality = TSK_CLAMP(1, (mp4ves->encoder.quality + 1), 31);
+ mp4ves->encoder.context->global_quality = FF_QP2LAMBDA * mp4ves->encoder.quality;
+ break;
+ }
+ case tmedia_codec_action_bw_up:
+ {
+ mp4ves->encoder.quality = TSK_CLAMP(1, (mp4ves->encoder.quality - 1), 31);
+ mp4ves->encoder.context->global_quality = FF_QP2LAMBDA * mp4ves->encoder.quality;
+ break;
+ }
+ }
+ }
+ else if(tsk_striequals(param->key, "rotation")){
+ int rotation = *((int32_t*)param->value);
+ if(mp4ves->encoder.rotation != rotation){
+ if(self->opened){
+ int ret;
+ mp4ves->encoder.rotation = rotation;
+ if((ret = tdav_codec_mp4ves_close_encoder(mp4ves))){
+ return ret;
+ }
+ if((ret = tdav_codec_mp4ves_open_encoder(mp4ves))){
+ return ret;
+ }
+ }
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int tdav_codec_mp4ves_open(tmedia_codec_t* self)
+{
+ int ret;
+
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+
+ if(!mp4v){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+
+ // Encoder
+ if((ret = tdav_codec_mp4ves_open_encoder(mp4v))){
+ return ret;
+ }
+
+ // Decoder
+ if((ret = tdav_codec_mp4ves_open_decoder(mp4v))){
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_mp4ves_close(tmedia_codec_t* self)
+{
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+
+ if(!mp4v){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+ // Encoder
+ tdav_codec_mp4ves_close_encoder(mp4v);
+
+ // Decoder
+ tdav_codec_mp4ves_close_decoder(mp4v);
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_mp4ves_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_mp4ves_t* mp4v = (tdav_codec_mp4ves_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)mp4v->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, mp4v->encoder.context->width, mp4v->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+
+ if(mp4v->encoder.force_idr){
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ mp4v->encoder.picture->pict_type = FF_I_TYPE;
+#else
+ mp4v->encoder.picture->pict_type = AV_PICTURE_TYPE_I;
+#endif
+ mp4v->encoder.force_idr = tsk_false;
+ }
+ else{
+ mp4v->encoder.picture->pict_type = 0;// reset
+ }
+ mp4v->encoder.picture->pts = AV_NOPTS_VALUE;
+ mp4v->encoder.picture->quality = mp4v->encoder.context->global_quality;
+ ret = avcodec_encode_video(mp4v->encoder.context, mp4v->encoder.buffer, size, mp4v->encoder.picture);
+ if(ret > 0){
+ tdav_codec_mp4ves_encap(mp4v, mp4v->encoder.buffer, (tsk_size_t)ret);
+ }
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_mp4ves_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_mp4ves_t* self = (tdav_codec_mp4ves_t*)_self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ if(!self || !in_data || !in_size || !out_data || !self->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // get expected size
+ xsize = avpicture_get_size(self->decoder.context->pix_fmt, self->decoder.context->width, self->decoder.context->height);
+
+ /* Packet lost? */
+ if(self->decoder.last_seq != (rtp_hdr->seq_num - 1) && self->decoder.last_seq){
+ if(self->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ self->decoder.last_seq = rtp_hdr->seq_num;
+
+ if((self->decoder.accumulator_pos + in_size) <= xsize){
+ memcpy(&((uint8_t*)self->decoder.accumulator)[self->decoder.accumulator_pos], in_data, in_size);
+ self->decoder.accumulator_pos += in_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ self->decoder.accumulator_pos = 0;
+ return 0;
+ }
+
+ if(rtp_hdr->marker){
+ AVPacket packet;
+ /* allocate destination buffer */
+ if(*out_max_size <xsize){
+ if(!(*out_data = tsk_realloc(*out_data, xsize))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ self->decoder.accumulator_pos = 0;
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = xsize;
+ }
+
+ av_init_packet(&packet);
+ packet.size = (int)self->decoder.accumulator_pos;
+ packet.data = self->decoder.accumulator;
+ ret = avcodec_decode_video2(self->decoder.context, self->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret < 0){
+ TSK_DEBUG_WARN("Failed to decode the buffer with error code = %d", ret);
+ if(TMEDIA_CODEC_VIDEO(self)->in.callback){
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+ else if(got_picture_ptr){
+ retsize = xsize;
+ TMEDIA_CODEC_VIDEO(self)->in.width = self->decoder.context->width;
+ TMEDIA_CODEC_VIDEO(self)->in.height = self->decoder.context->height;
+
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)self->decoder.picture, self->decoder.context->pix_fmt, (int)self->decoder.context->width, (int)self->decoder.context->height,
+ *out_data, (int)retsize);
+ }
+ /* in all cases: reset accumulator */
+ self->decoder.accumulator_pos = 0;
+ }
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_mp4ves_sdp_att_match(const tmedia_codec_t* _self, const char* att_name, const char* att_value)
+{
+ tdav_codec_mp4ves_t *self = (tdav_codec_mp4ves_t *)_self;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ if(tsk_striequals(att_name, "fmtp")){
+ tsk_params_L_t* params ;
+ /* e.g. profile-level-id=1; xx=yy */
+ if((params = tsk_params_fromstring(att_value, ";", tsk_true))){
+ int val_int;
+ if((val_int = tsk_params_get_param_value_as_int(params, "profile-level-id")) != -1){
+ TSK_DEBUG_INFO("Proposed profile-level-id=%d", val_int);
+ self->profile = val_int; // FIXME: Take the remote profile-level-id even if the bandwidth level doesn't match
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+ }
+
+ switch (self->profile ) {
+ case Simple_Profile_Level_1:
+ TMEDIA_CODEC_VIDEO(self)->out.width = TMEDIA_CODEC_VIDEO(self)->in.width = 176; TMEDIA_CODEC_VIDEO(self)->in.height = TMEDIA_CODEC_VIDEO(self)->out.height = 144;
+ break;
+ case Simple_Profile_Level_2:
+ case Simple_Profile_Level_3:
+ default:
+ TMEDIA_CODEC_VIDEO(self)->out.width = TMEDIA_CODEC_VIDEO(self)->in.width = 352; TMEDIA_CODEC_VIDEO(self)->in.height = TMEDIA_CODEC_VIDEO(self)->out.height = 288;
+ break;
+ }
+ }
+ else if(tsk_striequals(att_name, "imageattr")){
+ unsigned in_width, in_height, out_width, out_height;
+ if(tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(self)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0){
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(self)->in.width = in_width;
+ TMEDIA_CODEC_VIDEO(self)->in.height = in_height;
+ TMEDIA_CODEC_VIDEO(self)->out.width = out_width;
+ TMEDIA_CODEC_VIDEO(self)->out.height = out_height;
+ }
+
+ return tsk_true;
+}
+
+char* tdav_codec_mp4ves_sdp_att_get(const tmedia_codec_t* _self, const char* att_name)
+{
+ tdav_codec_mp4ves_t *self = (tdav_codec_mp4ves_t *)_self;
+
+ if(tsk_striequals(att_name, "fmtp")){
+ char* fmtp = tsk_null;
+ switch(_self->bl){//FIXME: deprecated
+ case tmedia_bl_low:
+ default:
+ self->profile = Simple_Profile_Level_1;
+ break;
+ case tmedia_bl_medium:
+ self->profile = Simple_Profile_Level_2;
+ break;
+ case tmedia_bl_hight:
+ case tmedia_bl_unrestricted:
+ self->profile = Simple_Profile_Level_3;
+ break;
+ }
+ tsk_sprintf(&fmtp, "profile-level-id=%d", self->profile);
+ return fmtp;
+ }
+ else if(tsk_striequals(att_name, "imageattr")){
+ return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(self)->pref_size,
+ TMEDIA_CODEC_VIDEO(self)->in.width, TMEDIA_CODEC_VIDEO(self)->in.height, TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height);
+ }
+ return tsk_null;
+}
+
+
+/* ============ Internal functions ================= */
+int tdav_codec_mp4ves_open_encoder(tdav_codec_mp4ves_t* self)
+{
+ int ret, size;
+ int32_t max_bw_kpbs;
+ if(!self->encoder.codec && !(self->encoder.codec = avcodec_find_encoder(CODEC_ID_MPEG4))){
+ TSK_DEBUG_ERROR("Failed to find mp4v encoder");
+ return -1;
+ }
+
+ if(self->encoder.context){
+ TSK_DEBUG_ERROR("Encoder already opened");
+ return -1;
+ }
+ self->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->encoder.context);
+
+ self->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->encoder.context->time_base.num = 1;
+ self->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(self)->in.fps;
+ self->encoder.context->width = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.context->height = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->encoder.context->mb_decision = FF_MB_DECISION_RD;
+ self->encoder.context->noise_reduction = 250;
+ self->encoder.context->flags |= CODEC_FLAG_QSCALE;
+ self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality;
+
+ max_bw_kpbs = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
+ self->encoder.max_bw_kpbs
+ );
+ self->encoder.context->bit_rate = (max_bw_kpbs * 1024);// bps
+ self->encoder.context->rtp_payload_size = MP4V_RTP_PAYLOAD_SIZE;
+ self->encoder.context->opaque = tsk_null;
+ self->encoder.context->profile = self->profile>>4;
+ self->encoder.context->level = self->profile & 0x0F;
+ self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->in.fps * MP4V_GOP_SIZE_IN_SECONDS);
+ self->encoder.context->max_b_frames = 0;
+ self->encoder.context->b_frame_strategy = 1;
+ self->encoder.context->flags |= CODEC_FLAG_AC_PRED;
+
+ // Picture (YUV 420)
+ if(!(self->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create MP4V-ES encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->encoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height);
+ if(!(self->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate MP4V-ES encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open MP4V-ES encoder");
+ return ret;
+ }
+
+ TSK_DEBUG_INFO("[MP4V-ES] bitrate=%d bps", self->encoder.context->bit_rate);
+
+ return ret;
+}
+
+int tdav_codec_mp4ves_open_decoder(tdav_codec_mp4ves_t* self)
+{
+ int ret, size;
+
+ if(!self->decoder.codec && !(self->decoder.codec = avcodec_find_decoder(CODEC_ID_MPEG4))){
+ TSK_DEBUG_ERROR("Failed to find MP4V-ES decoder");
+ return -1;
+ }
+
+ if(self->decoder.context){
+ TSK_DEBUG_ERROR("Decoder already opened");
+ return -1;
+ }
+
+ self->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->decoder.context);
+
+ self->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->decoder.context->width = TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->decoder.context->height = TMEDIA_CODEC_VIDEO(self)->out.height;
+
+ // Picture (YUV 420)
+ if(!(self->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->decoder.context->width, self->decoder.context->height);
+ if(!(self->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ if(!(self->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ if((ret = avcodec_open(self->decoder.context, self->decoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open MP4V-ES decoder");
+ return ret;
+ }
+
+ self->decoder.last_seq = 0;
+
+ return ret;
+}
+
+int tdav_codec_mp4ves_close_encoder(tdav_codec_mp4ves_t* self)
+{
+ if(self->encoder.context){
+ avcodec_close(self->encoder.context);
+ av_free(self->encoder.context);
+ self->encoder.context = tsk_null;
+ }
+ if(self->encoder.picture){
+ av_free(self->encoder.picture);
+ }
+ if(self->encoder.buffer){
+ TSK_FREE(self->encoder.buffer);
+ }
+ return 0;
+}
+
+int tdav_codec_mp4ves_close_decoder(tdav_codec_mp4ves_t* self)
+{
+ if(self->decoder.context){
+ avcodec_close(self->decoder.context);
+ if(self->decoder.context->extradata){
+ TSK_FREE(self->decoder.context->extradata);
+ self->decoder.context->extradata_size = 0;
+ }
+ av_free(self->decoder.context);
+ self->decoder.context = tsk_null;
+ }
+ if(self->decoder.picture){
+ av_free(self->decoder.picture);
+ self->decoder.picture = tsk_null;
+ }
+ if(self->decoder.accumulator){
+ TSK_FREE(self->decoder.accumulator);
+ }
+
+ return 0;
+}
+
+static void tdav_codec_mp4ves_encap(tdav_codec_mp4ves_t* mp4v, const uint8_t* pdata, tsk_size_t size)
+{
+ uint32_t scode; // start code
+
+ if(size <= 4/*32bits: start code size*/){
+ TSK_DEBUG_ERROR("Too short");
+ return;
+ }
+ // first 32bits
+ scode = tnet_htonl_2(pdata);
+
+/* RFC 3016 - 3.3 Examples of packetized MPEG-4 Visual bitstream
+
+ VS= Visual Object Sequence
+ VO= Visual Object
+ VOL= Visual Object Layer
+ VOP= Visual Object Plane
+ GOV= Group of Visual Object Plane
+ VP= Video Plane
+
+ +------+------+------+------+
+(a) | RTP | VS | VO | VOL |
+ |header|header|header|header|
+ +------+------+------+------+
+
+ +------+------+------+------+------------+
+(b) | RTP | VS | VO | VOL |Video Packet|
+ |header|header|header|header| |
+ +------+------+------+------+------------+
+
+ +------+-----+------------------+
+(c) | RTP | GOV |Video Object Plane|
+ |header| | |
+ +------+-----+------------------+
+
+ +------+------+------------+ +------+------+------------+
+(d) | RTP | VOP |Video Packet| | RTP | VP |Video Packet|
+ |header|header| (1) | |header|header| (2) |
+ +------+------+------------+ +------+------+------------+
+
+ +------+------+------------+------+------------+------+------------+
+(e) | RTP | VP |Video Packet| VP |Video Packet| VP |Video Packet|
+ |header|header| (1) |header| (2) |header| (3) |
+ +------+------+------------+------+------------+------+------------+
+
+ +------+------+------------+ +------+------------+
+(f) | RTP | VOP |VOP fragment| | RTP |VOP fragment|
+ |header|header| (1) | |header| (2) | ___
+ +------+------+------------+ +------+------------+
+
+ Figure 2 - Examples of RTP packetized MPEG-4 Visual bitstream
+*/
+
+/* RFC 3016 - 3.2 Fragmentation of MPEG-4 Visual bitstream
+
+ A fragmented MPEG-4 Visual bitstream is mapped directly onto the RTP
+ payload without any addition of extra header fields or any removal of
+ Visual syntax elements. The Combined Configuration/Elementary
+ streams mode is used.
+
+ In the following, header means one of the following:
+
+ - Configuration information (Visual Object Sequence Header, Visual
+ Object Header and Video Object Layer Header)
+ - visual_object_sequence_end_code
+ - The header of the entry point function for an elementary stream
+ (Group_of_VideoObjectPlane() or the header of VideoObjectPlane(),
+ video_plane_with_short_header(), MeshObject() or FaceObject())
+ - The video packet header (video_packet_header() excluding
+ next_resync_marker())
+ - The header of gob_layer()
+ See 6.2.1 "Start codes" of ISO/IEC 14496-2 [2][9][4] for the
+ definition of the configuration information and the entry point
+ functions.
+*/
+
+ switch(scode){
+ case visual_object_sequence_start_code:
+ case visual_object_start_code:
+ case user_data_start_code:
+ case video_object_layer_start_code:
+ case group_of_vop_start_code:
+ case vop_start_code:
+ {
+ register uint32_t i, last_index = 0;
+ int startcode = 0xffffffff;
+
+ if(scode == visual_object_sequence_start_code && size >=5){
+ //uint8_t profile_and_level_indication = pdata[4]; /* IEC 14496-2: 6.3.2 Visual Object Sequence and Visual Object */
+ // TSK_DEBUG_INFO("profile_and_level_indication=%d", profile_and_level_indication);
+ }
+
+ if(size < MP4V_RTP_PAYLOAD_SIZE){
+ goto last;
+ }
+
+ for(i = 4; i<(size - 4); i++){
+ startcode = (startcode <<8) | pdata[i];
+ switch(startcode){
+ case visual_object_sequence_start_code:
+ case group_of_vop_start_code:
+ case vop_start_code:
+ tdav_codec_mp4ves_rtp_callback(mp4v, pdata + last_index, (i - last_index), (last_index == size));
+ last_index = i;
+ }
+ }
+last:
+ if(last_index < size){
+ tdav_codec_mp4ves_rtp_callback(mp4v, pdata + last_index, (size - last_index), tsk_true);
+ }
+ break;
+ }
+ default:
+ TSK_DEBUG_ERROR("%x is an invalide start code", scode);
+ break;
+ }
+}
+
+static void tdav_codec_mp4ves_rtp_callback(tdav_codec_mp4ves_t *mp4v, const void *data, tsk_size_t size, tsk_bool_t marker)
+{
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(mp4v)->out.callback){
+ TMEDIA_CODEC_VIDEO(mp4v)->out.result.buffer.ptr = data;
+ TMEDIA_CODEC_VIDEO(mp4v)->out.result.buffer.size = size;
+ TMEDIA_CODEC_VIDEO(mp4v)->out.result.duration = (uint32_t)((1./(double)TMEDIA_CODEC_VIDEO(mp4v)->out.fps) * TMEDIA_CODEC(mp4v)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(mp4v)->out.result.last_chunck = marker;
+ TMEDIA_CODEC_VIDEO(mp4v)->out.callback(&TMEDIA_CODEC_VIDEO(mp4v)->out.result);
+ }
+}
+
+
+/* ============ MP4V-ES Plugin interface ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_mp4ves_ctor(tsk_object_t * _self, va_list * app)
+{
+ tdav_codec_mp4ves_t *self = _self;
+ if(self){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ self->profile = DEFAULT_PROFILE_LEVEL_ID;
+ self->encoder.quality = 1;
+ self->encoder.max_bw_kpbs = tmedia_defaults_get_bandwidth_video_upload_max();
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_mp4ves_dtor(tsk_object_t * _self)
+{
+ tdav_codec_mp4ves_t *self = _self;
+ if(self){
+ /* deinit base */
+ tmedia_codec_video_deinit(self); // will close the codec if opened
+ /* deinit self */
+ TSK_FREE(self->rtp.ptr);
+ self->rtp.size = 0;
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_mp4ves_def_s =
+{
+ sizeof(tdav_codec_mp4ves_t),
+ tdav_codec_mp4ves_ctor,
+ tdav_codec_mp4ves_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_mp4ves_plugin_def_s =
+{
+ &tdav_codec_mp4ves_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_mp4ves_es,
+ "MP4V-ES",
+ "MP4V-ES Codec",
+ TMEDIA_CODEC_FORMAT_MP4V_ES,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps) */
+ {176, 144, 0},// fps is @deprecated
+
+ tdav_codec_mp4ves_set,
+ tdav_codec_mp4ves_open,
+ tdav_codec_mp4ves_close,
+ tdav_codec_mp4ves_encode,
+ tdav_codec_mp4ves_decode,
+ tdav_codec_mp4ves_sdp_att_match,
+ tdav_codec_mp4ves_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_mp4ves_plugin_def_t = &tdav_codec_mp4ves_plugin_def_s;
+
+tsk_bool_t tdav_codec_ffmpeg_mp4ves_is_supported()
+{
+ return (avcodec_find_encoder(CODEC_ID_MPEG4) && avcodec_find_decoder(CODEC_ID_MPEG4));
+}
+
+#endif /* HAVE_FFMPEG */
+
diff --git a/tinyDAV/src/codecs/msrp/tdav_codec_msrp.c b/tinyDAV/src/codecs/msrp/tdav_codec_msrp.c
new file mode 100644
index 0000000..5b72ded
--- /dev/null
+++ b/tinyDAV/src/codecs/msrp/tdav_codec_msrp.c
@@ -0,0 +1,106 @@
+/*
+* Copyright (C) 2010-2011 Mamadou Diop.
+*
+*
+* 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_msrp.c
+ * @brief The Message Session Relay Protocol (MSRP) fake codec.
+ * Used for both Message (RFC 4975) and file transfer (RFC 5547).
+ *
+ */
+#include "tinydav/codecs/msrp/tdav_codec_msrp.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ MSRP Plugin interface ================= */
+#define tdav_codec_msrp_open tsk_null
+#define tdav_codec_msrp_close tsk_null
+#define tdav_codec_msrp_sdp_att_get tsk_null
+#define tdav_codec_msrp_sdp_att_get tsk_null
+#define tdav_codec_msrp_encode tsk_null
+#define tdav_codec_msrp_decode tsk_null
+
+static tsk_bool_t tdav_codec_msrp_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{ /* always match */
+ return tsk_true;
+}
+
+//
+// MSRP Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_msrp_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_msrp_t *msrp = self;
+ if(msrp){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_msrp_dtor(tsk_object_t * self)
+{
+ tdav_codec_msrp_t *msrp = self;
+ if(msrp){
+ /* deinit base */
+ tmedia_codec_msrp_deinit(msrp);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_msrp_def_s =
+{
+ sizeof(tdav_codec_msrp_t),
+ tdav_codec_msrp_ctor,
+ tdav_codec_msrp_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_msrp_plugin_def_s =
+{
+ &tdav_codec_msrp_def_s,
+
+ tmedia_msrp,
+ tmedia_codec_id_none, // fake codec without real id
+ "message",
+ "MSRP fake codec",
+ TMEDIA_CODEC_FORMAT_MSRP,
+ tsk_false,
+ 0, // rate
+
+ /* audio */
+ {0},
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_msrp_open,
+ tdav_codec_msrp_close,
+ tdav_codec_msrp_encode,
+ tdav_codec_msrp_decode,
+ tdav_codec_msrp_sdp_att_match,
+ tdav_codec_msrp_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_msrp_plugin_def_t = &tdav_codec_msrp_plugin_def_s;
diff --git a/tinyDAV/src/codecs/opus/tdav_codec_opus.c b/tinyDAV/src/codecs/opus/tdav_codec_opus.c
new file mode 100644
index 0000000..355fc73
--- /dev/null
+++ b/tinyDAV/src/codecs/opus/tdav_codec_opus.c
@@ -0,0 +1,363 @@
+/*
+* Copyright (C) 2010-2013 Doubango Telecom <http://www.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_opus.c
+ * @brief OPUS audio codec.
+ * SDP: http://tools.ietf.org/html/draft-spittka-payload-rtp-opus-03
+ */
+#include "tinydav/codecs/opus/tdav_codec_opus.h"
+
+#if HAVE_LIBOPUS
+
+#include <opus/opus.h>
+
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_string.h"
+#include "tsk_debug.h"
+
+#if !defined(TDAV_OPUS_MAX_FRAME_SIZE_IN_SAMPLES)
+# define TDAV_OPUS_MAX_FRAME_SIZE_IN_SAMPLES (5760) /* 120ms@48kHz */
+#endif
+#if !defined(TDAV_OPUS_MAX_FRAME_SIZE_IN_BYTES)
+# define TDAV_OPUS_MAX_FRAME_SIZE_IN_BYTES (TDAV_OPUS_MAX_FRAME_SIZE_IN_SAMPLES << 1) /* 120ms@48kHz */
+#endif
+#if !defined(TDAV_OPUS_FEC_ENABLED)
+# define TDAV_OPUS_FEC_ENABLED 0
+#endif
+#if !defined(TDAV_OPUS_DTX_ENABLED)
+# define TDAV_OPUS_DTX_ENABLED 0
+#endif
+
+typedef struct tdav_codec_opus_s
+{
+ TMEDIA_DECLARE_CODEC_AUDIO;
+
+ struct {
+ OpusEncoder *inst;
+ } encoder;
+
+ struct {
+ OpusDecoder *inst;
+ opus_int16 buff[TDAV_OPUS_MAX_FRAME_SIZE_IN_SAMPLES];
+ tsk_bool_t fec_enabled;
+ tsk_bool_t dtx_enabled;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_opus_t;
+
+
+static tsk_bool_t _tdav_codec_opus_rate_is_valid(const int32_t rate)
+{
+ switch(rate){
+ case 8000: case 12000: case 16000: case 24000: case 48000: return tsk_true;
+ default: return tsk_false;
+ }
+}
+
+static int tdav_codec_opus_open(tmedia_codec_t* self)
+{
+ tdav_codec_opus_t* opus = (tdav_codec_opus_t*)self;
+ int opus_err;
+
+ if(!opus){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ // Initialize the decoder
+ if(!opus->decoder.inst){
+ TSK_DEBUG_INFO("[OPUS] Open decoder: rate=%d, channels=%d", (int)self->in.rate, (int)TMEDIA_CODEC_AUDIO(self)->in.channels);
+ if(!(opus->decoder.inst = opus_decoder_create((opus_int32)self->in.rate, (int)TMEDIA_CODEC_AUDIO(self)->in.channels, &opus_err)) || opus_err != OPUS_OK){
+ TSK_DEBUG_ERROR("Failed to create Opus decoder(rate=%d, channels=%d) instance with error code=%d.", (int)self->in.rate, (int)TMEDIA_CODEC_AUDIO(self)->in.channels, opus_err);
+ return -2;
+ }
+ }
+ opus->decoder.last_seq = 0;
+
+ // Initialize the encoder
+ if(!opus->encoder.inst){
+ TSK_DEBUG_INFO("[OPUS] Open encoder: rate=%d, channels=%d", (int)self->out.rate, (int)TMEDIA_CODEC_AUDIO(self)->out.channels);
+ if(!(opus->encoder.inst = opus_encoder_create((opus_int32)self->out.rate, (int)TMEDIA_CODEC_AUDIO(self)->out.channels, OPUS_APPLICATION_VOIP, &opus_err)) || opus_err != OPUS_OK){
+ TSK_DEBUG_ERROR("Failed to create Opus decoder(rate=%d, channels=%d) instance with error code=%d.", (int)self->out.rate, (int)TMEDIA_CODEC_AUDIO(self)->out.channels, opus_err);
+ return -2;
+ }
+ }
+#if TDAV_UNDER_MOBILE /* iOS, Android and WP8 */
+ opus_encoder_ctl(opus->encoder.inst, OPUS_SET_COMPLEXITY(3));
+#endif
+ opus_encoder_ctl(opus->encoder.inst, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
+
+ return 0;
+}
+
+static int tdav_codec_opus_close(tmedia_codec_t* self)
+{
+ tdav_codec_opus_t* opus = (tdav_codec_opus_t*)self;
+
+ (void)(opus);
+
+ /* resources will be freed by the dctor() */
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_opus_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_opus_t* opus = (tdav_codec_opus_t*)self;
+ opus_int32 ret;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(!opus->encoder.inst){
+ TSK_DEBUG_ERROR("Encoder not ready");
+ return 0;
+ }
+
+ // we're sure that the output (encoded) size cannot be higher than the input (raw)
+ if(*out_max_size < in_size){
+ if(!(*out_data = tsk_realloc(*out_data, in_size))){
+ TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", in_size);
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = in_size;
+ }
+
+ ret = opus_encode(opus->encoder.inst,
+ (const opus_int16 *)in_data, (int)(in_size >> 1),
+ (unsigned char *)*out_data, (opus_int32)*out_max_size);
+
+ if(ret < 0){
+ TSK_DEBUG_ERROR("opus_encode() failed with error code = %d", ret);
+ return 0;
+ }
+
+ return (tsk_size_t)ret;
+}
+
+static tsk_size_t tdav_codec_opus_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_opus_t* opus = (tdav_codec_opus_t*)self;
+ int frame_size;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if(!opus->decoder.inst){
+ TSK_DEBUG_ERROR("Decoder not ready");
+ return 0;
+ }
+
+ /* Packet loss? */
+ if(opus->decoder.last_seq != (rtp_hdr->seq_num - 1) && opus->decoder.last_seq){
+ if(opus->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("[Opus] Packet loss, seq_num=%d", rtp_hdr->seq_num);
+ opus_decode(opus->decoder.inst, tsk_null/*packet loss*/, (opus_int32)0, opus->decoder.buff, TDAV_OPUS_MAX_FRAME_SIZE_IN_SAMPLES, opus->decoder.fec_enabled);
+ }
+ opus->decoder.last_seq = rtp_hdr->seq_num;
+
+ frame_size = opus_decode(opus->decoder.inst, (const unsigned char *)in_data, (opus_int32)in_size, opus->decoder.buff, TDAV_OPUS_MAX_FRAME_SIZE_IN_SAMPLES, opus->decoder.fec_enabled ? 1 : 0);
+ if(frame_size > 0){
+ tsk_size_t frame_size_inbytes = (frame_size << 1);
+ if(*out_max_size < frame_size_inbytes){
+ if(!(*out_data = tsk_realloc(*out_data, frame_size_inbytes))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ *out_max_size = frame_size_inbytes;
+ }
+ memcpy(*out_data, opus->decoder.buff, frame_size_inbytes);
+ return frame_size_inbytes;
+ }
+ else{
+ return 0;
+ }
+}
+
+static tsk_bool_t tdav_codec_opus_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ tdav_codec_opus_t* opus = (tdav_codec_opus_t*)codec;
+
+ if(!opus){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ TSK_DEBUG_INFO("[OPUS] Trying to match [%s:%s]", att_name, att_value);
+
+ if(tsk_striequals(att_name, "fmtp")){
+ int val_int;
+ tsk_params_L_t* params;
+ /* e.g. FIXME */
+ if((params = tsk_params_fromstring(att_value, ";", tsk_true))){
+ tsk_bool_t ret = tsk_false;
+ /* === maxplaybackrate ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "maxplaybackrate")) != -1){
+ if(!_tdav_codec_opus_rate_is_valid(val_int)){
+ TSK_DEBUG_ERROR("[OPUS] %d not valid as maxplaybackrate value", val_int);
+ goto done;
+ }
+ TMEDIA_CODEC(opus)->out.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->out.rate, val_int);
+ TMEDIA_CODEC_AUDIO(opus)->out.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->out.rate);
+ }
+ /* === sprop-maxcapturerate ===*/
+ if((val_int = tsk_params_get_param_value_as_int(params, "sprop-maxcapturerate")) != -1){
+ if(!_tdav_codec_opus_rate_is_valid(val_int)){
+ TSK_DEBUG_ERROR("[OPUS] %d not valid as sprop-maxcapturerate value", val_int);
+ goto done;
+ }
+ TMEDIA_CODEC(opus)->in.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->in.rate, val_int);
+ TMEDIA_CODEC_AUDIO(opus)->in.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->in.rate);
+ }
+ ret = tsk_true;
+done:
+ TSK_OBJECT_SAFE_FREE(params);
+ return ret;
+ }
+ }
+
+ return tsk_true;
+}
+
+static char* tdav_codec_opus_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ tdav_codec_opus_t* opus = (tdav_codec_opus_t*)codec;
+
+ if(!opus){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ if(tsk_striequals(att_name, "fmtp")){
+ char* fmtp = tsk_null;
+ tsk_sprintf(&fmtp, "maxplaybackrate=%d; sprop-maxcapturerate=%d; stereo=%d; sprop-stereo=%d; useinbandfec=%d; usedtx=%d",
+ TMEDIA_CODEC(opus)->in.rate,
+ TMEDIA_CODEC(opus)->out.rate,
+ (TMEDIA_CODEC_AUDIO(opus)->in.channels == 2) ? 1 : 0,
+ (TMEDIA_CODEC_AUDIO(opus)->out.channels == 2) ? 1 : 0,
+ opus->decoder.fec_enabled ? 1 : 0,
+ opus->decoder.dtx_enabled ? 1 : 0
+ );
+ return fmtp;
+ }
+
+ return tsk_null;
+}
+
+//
+// OPUS Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_opus_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_opus_t *opus = self;
+ if(opus){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ TMEDIA_CODEC(opus)->in.rate = tmedia_defaults_get_opus_maxplaybackrate();
+ TMEDIA_CODEC(opus)->out.rate = tmedia_defaults_get_opus_maxcapturerate();
+ TMEDIA_CODEC_AUDIO(opus)->in.channels = 1;
+ TMEDIA_CODEC_AUDIO(opus)->out.channels = 1;
+#if TDAV_OPUS_FEC_ENABLED
+ opus->decoder.fec_enabled = tsk_true;
+#endif
+#if TDAV_OPUS_DTX_ENABLED
+ opus->decoder.dtx_enabled = tsk_true;
+#endif
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_opus_dtor(tsk_object_t * self)
+{
+ tdav_codec_opus_t *opus = self;
+ if(opus){
+ /* deinit base */
+ tmedia_codec_audio_deinit(opus);
+ /* deinit self */
+ if(opus->decoder.inst){
+ opus_decoder_destroy(opus->decoder.inst), opus->decoder.inst = tsk_null;
+ }
+ if(opus->encoder.inst){
+ opus_encoder_destroy(opus->encoder.inst), opus->encoder.inst = tsk_null;
+ }
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_opus_def_s =
+{
+ sizeof(tdav_codec_opus_t),
+ tdav_codec_opus_ctor,
+ tdav_codec_opus_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_opus_plugin_def_s =
+{
+ &tdav_codec_opus_def_s,
+
+ tmedia_audio,
+ tmedia_codec_id_opus,
+ "opus",
+ "opus Codec",
+ TMEDIA_CODEC_FORMAT_OPUS,
+ tsk_true,
+ 48000, // this is the default sample rate
+
+ { /* audio */
+ 2, // channels
+ 0 // ptime @deprecated
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // set()
+ tdav_codec_opus_open,
+ tdav_codec_opus_close,
+ tdav_codec_opus_encode,
+ tdav_codec_opus_decode,
+ tdav_codec_opus_sdp_att_match,
+ tdav_codec_opus_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_opus_plugin_def_t = &tdav_codec_opus_plugin_def_s;
+
+
+#endif /* HAVE_LIBOPUS */
diff --git a/tinyDAV/src/codecs/speex/tdav_codec_speex.c b/tinyDAV/src/codecs/speex/tdav_codec_speex.c
new file mode 100644
index 0000000..18c4440
--- /dev/null
+++ b/tinyDAV/src/codecs/speex/tdav_codec_speex.c
@@ -0,0 +1,286 @@
+/*
+* 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_speex.c
+ * @brief Speex codecs
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/speex/tdav_codec_speex.h"
+
+#if HAVE_LIB_SPEEX
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define SPEEX_BUFFER_MAX_SIZE 1024
+#define SPEEX_DEFAULT_QUALITY 6
+
+/* ============ Common ================= */
+int tdav_codec_speex_init(tdav_codec_speex_t* self, tdav_codec_speex_type_t type);
+int tdav_codec_speex_deinit(tdav_codec_speex_t* self);
+
+/* ============ Speex Plugin interface ================= */
+
+int tdav_codec_speex_open(tmedia_codec_t* self)
+{
+ static int quality = SPEEX_DEFAULT_QUALITY;
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+
+ switch(speex->type){
+ case tdav_codec_speex_type_nb:
+ speex->encoder.state = speex_encoder_init(&speex_nb_mode);
+ speex->decoder.state = speex_decoder_init(&speex_nb_mode);
+ break;
+ case tdav_codec_speex_type_wb:
+ speex->encoder.state = speex_encoder_init(&speex_wb_mode);
+ speex->decoder.state = speex_decoder_init(&speex_wb_mode);
+ break;
+ case tdav_codec_speex_type_uwb:
+ speex->encoder.state = speex_encoder_init(&speex_uwb_mode);
+ speex->decoder.state = speex_decoder_init(&speex_uwb_mode);
+ break;
+ default:
+ TSK_DEBUG_ERROR("Not implemented");
+ return -2;
+ }
+
+ speex_decoder_ctl(speex->decoder.state, SPEEX_GET_FRAME_SIZE, &speex->decoder.size);
+ speex->decoder.size *= sizeof(spx_int16_t);
+ if(!(speex->decoder.buffer = tsk_calloc(speex->decoder.size, 1))){
+ speex->decoder.size = speex->decoder.size = 0;
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return -3;
+ }
+
+ speex_encoder_ctl(speex->encoder.state, SPEEX_SET_QUALITY, &quality);
+ speex_encoder_ctl(speex->encoder.state, SPEEX_GET_FRAME_SIZE, &speex->encoder.size);
+
+ speex_bits_init(&speex->encoder.bits);
+ speex_bits_init(&speex->decoder.bits);
+ speex_bits_reset(&speex->encoder.bits);
+ speex_bits_reset(&speex->decoder.bits);
+
+ return 0;
+}
+
+int tdav_codec_speex_close(tmedia_codec_t* self)
+{
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+
+ (void)(speex);
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_speex_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_speex_t* speex = (tdav_codec_speex_t*)self;
+ tsk_size_t outsize = 0;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ speex_bits_reset(&speex->encoder.bits);
+ speex_encode_int(speex->encoder.state, (spx_int16_t*)in_data, &speex->encoder.bits);
+
+ if(*out_max_size <speex->encoder.size){
+ if((*out_data = tsk_realloc(*out_data, speex->encoder.size))){
+ *out_max_size = speex->encoder.size;
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+
+ outsize = speex_bits_write(&speex->encoder.bits, *out_data, (speex->encoder.size >> 1));
+
+ return outsize;
+}
+
+tsk_size_t tdav_codec_speex_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)
+{
+ int ret;
+ tsk_size_t out_size = 0;
+ tdav_codec_speex_t* speex = (tdav_codec_speex_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // initializes the bit-stream
+ speex_bits_read_from(&speex->decoder.bits, (char*)in_data, in_size);
+
+ do{
+ // performs decode()
+ if((ret = speex_decode_int(speex->decoder.state, &speex->decoder.bits, speex->decoder.buffer))){
+ TSK_DEBUG_ERROR("Failed to decode the buffer. retcode=%d", ret);
+ break;
+ }
+
+ if(*out_max_size <(out_size + speex->decoder.size)){
+ if((*out_data = tsk_realloc(*out_data, (out_size + speex->decoder.size)))){
+ *out_max_size = (out_size + speex->decoder.size);
+ }
+ else{
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+
+ // copy output buffer
+ memcpy(&((uint8_t*)*out_data)[out_size], speex->decoder.buffer, speex->decoder.size);
+ out_size += speex->decoder.size;
+ }
+ while(speex_bits_remaining(&speex->decoder.bits) >= 5);
+
+
+ return out_size;
+}
+
+char* tdav_codec_speex_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ return tsk_null;
+}
+
+tsk_bool_t tdav_codec_speex_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ return tsk_true;
+}
+
+
+//
+// Speex Codec Object definition
+//
+#define SPEEX_OBJECT_DEFINITION(mode,name,description,format,rate) \
+ static tsk_object_t* tdav_codec_speex_##mode##_ctor(tsk_object_t * self, va_list * app) \
+ { \
+ tdav_codec_speex_t *speex = self; \
+ if(speex){ \
+ tdav_codec_speex_init(speex, tdav_codec_speex_type_##mode); \
+ } \
+ return self; \
+ } \
+ static tsk_object_t* tdav_codec_speex_##mode##_dtor(tsk_object_t * self) \
+ { \
+ tdav_codec_speex_t *speex = self; \
+ if(speex){ \
+ /* deinit base */ \
+ tmedia_codec_audio_deinit(speex); \
+ /* deinit self */ \
+ tdav_codec_speex_deinit(speex); \
+ } \
+ \
+ return self; \
+ } \
+ static const tsk_object_def_t tdav_codec_speex_##mode##_def_s = \
+ { \
+ sizeof(tdav_codec_speex_t), \
+ tdav_codec_speex_##mode##_ctor, \
+ tdav_codec_speex_##mode##_dtor, \
+ tmedia_codec_cmp, \
+ }; \
+ static const tmedia_codec_plugin_def_t tdav_codec_speex_##mode##_plugin_def_s = \
+ { \
+ &tdav_codec_speex_##mode##_def_s, \
+ \
+ tmedia_audio, \
+ tmedia_codec_id_speex_##mode, \
+ name, \
+ description, \
+ format, \
+ tsk_true, \
+ rate, /* rate*/ \
+ \
+ { /* audio */ \
+ 1, /* channels*/ \
+ 0 /* ptime @deprecated*/ \
+ }, \
+ \
+ /* video */ \
+ {0}, \
+ \
+ tsk_null, /* set()*/ \
+ tdav_codec_speex_open, \
+ tdav_codec_speex_close, \
+ tdav_codec_speex_encode, \
+ tdav_codec_speex_decode, \
+ tdav_codec_speex_sdp_att_match, \
+ tdav_codec_speex_sdp_att_get \
+ }; \
+ const tmedia_codec_plugin_def_t *tdav_codec_speex_##mode##_plugin_def_t = &tdav_codec_speex_##mode##_plugin_def_s;
+
+
+SPEEX_OBJECT_DEFINITION(nb,"SPEEX","Speex-NB Codec",TMEDIA_CODEC_FORMAT_SPEEX_NB,8000);
+SPEEX_OBJECT_DEFINITION(wb,"SPEEX","Speex-WB Codec",TMEDIA_CODEC_FORMAT_SPEEX_WB,16000);
+SPEEX_OBJECT_DEFINITION(uwb,"SPEEX","Speex-UWB Codec",TMEDIA_CODEC_FORMAT_SPEEX_UWB,32000);
+
+//
+// Common functions
+//
+int tdav_codec_speex_init(tdav_codec_speex_t* self, tdav_codec_speex_type_t type)
+{
+ if(self){
+ self->type = type;
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+}
+
+int tdav_codec_speex_deinit(tdav_codec_speex_t* self)
+{
+ if(self){
+ if(self->decoder.state){
+ speex_decoder_destroy(self->decoder.state);
+ self->decoder.state = tsk_null;
+ }
+ speex_bits_destroy(&self->decoder.bits);
+ if(self->decoder.buffer){
+ TSK_FREE(self->decoder.buffer);
+ self->decoder.size = 0;
+ }
+
+ if(self->encoder.state){
+ speex_encoder_destroy(self->encoder.state);
+ self->encoder.state = tsk_null;
+ }
+ speex_bits_destroy(&self->encoder.bits);
+ self->encoder.size = 0;
+
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+}
+
+#endif /* HAVE_LIB_SPEEX */
diff --git a/tinyDAV/src/codecs/t140/tdav_codec_t140.c b/tinyDAV/src/codecs/t140/tdav_codec_t140.c
new file mode 100644
index 0000000..b401321
--- /dev/null
+++ b/tinyDAV/src/codecs/t140/tdav_codec_t140.c
@@ -0,0 +1,175 @@
+/*
+* Copyright (C) 2012 Mamadou Diop.
+*
+* 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_t140.c
+ * @brief T140 codec implementation (RFC 4103)
+ */
+#include "tinydav/codecs/t140/tdav_codec_t140.h"
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+// RFC 4103 - 6. Parameter for Character Transmission Rate
+#define TDAV_CODEC_T140_CPS 30
+
+static int tdav_codec_t140_set(tmedia_codec_t* self, const struct tmedia_param_s* param)
+{
+ return 0;
+}
+
+static int tdav_codec_t140_open(tmedia_codec_t* self)
+{
+ return 0;
+}
+
+static int tdav_codec_t140_close(tmedia_codec_t* self)
+{
+ return 0;
+}
+
+
+static tsk_size_t tdav_codec_t140_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ tsk_size_t out_size = in_size;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+ memcpy(*out_data, in_data, out_size);
+
+ return out_size;
+}
+
+static tsk_size_t tdav_codec_t140_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)
+{
+ tsk_size_t out_size = in_size;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* allocate new 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 = 0;
+ return 0;
+ }
+ *out_max_size = out_size;
+ }
+ memcpy(*out_data, in_data, out_size);
+
+ return out_size;
+}
+
+static tsk_bool_t tdav_codec_t140_sdp_att_match(const tmedia_codec_t* self, const char* att_name, const char* att_value)
+{
+ return tsk_true;
+}
+
+static char* tdav_codec_t140_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
+{
+ if(!self || !att_name){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ if(tsk_striequals("fmtp", att_name)){
+ char* fmtp = tsk_null;
+ tsk_sprintf(&fmtp, "cps=%d", TDAV_CODEC_T140_CPS);
+ return fmtp;
+ }
+ return tsk_null;
+}
+
+
+//
+// G.711u Plugin definition
+//
+
+/* constructor */
+static tsk_object_t* tdav_codec_t140_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_t140_t *t140 = self;
+ if(t140){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_t140_dtor(tsk_object_t * self)
+{
+ tdav_codec_t140_t *t140 = self;
+ if(t140){
+ /* deinit base */
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_t140_def_s =
+{
+ sizeof(tdav_codec_t140_t),
+ tdav_codec_t140_ctor,
+ tdav_codec_t140_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_t140_plugin_def_s =
+{
+ &tdav_codec_t140_def_s,
+
+ tmedia_t140,
+ tmedia_codec_id_t140,
+ "t140",
+ "T140 codec (From tinyDAV)",
+ TMEDIA_CODEC_FORMAT_T140,
+ tsk_true,
+ 1000, // rate
+
+ /* audio */
+ {0},
+
+ /* video */
+ {0},
+
+ tdav_codec_t140_set,
+ tdav_codec_t140_open,
+ tdav_codec_t140_close,
+ tdav_codec_t140_encode,
+ tdav_codec_t140_decode,
+ tdav_codec_t140_sdp_att_match,
+ tdav_codec_t140_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_t140_plugin_def_t = &tdav_codec_t140_plugin_def_s;
diff --git a/tinyDAV/src/codecs/theora/tdav_codec_theora.c b/tinyDAV/src/codecs/theora/tdav_codec_theora.c
new file mode 100644
index 0000000..01072c3
--- /dev/null
+++ b/tinyDAV/src/codecs/theora/tdav_codec_theora.c
@@ -0,0 +1,862 @@
+/*
+* 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_theora.c
+ * @brief Theora codec plugin
+ * RTP payloader/depayloader follows draft-barbato-avt-rtp-theora-01.
+ * For more information about Theora, http://www.theora.org/doc/Theora.pdf.
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+
+ */
+#include "tinydav/codecs/theora/tdav_codec_theora.h"
+
+#if HAVE_FFMPEG
+
+#include "tinydav/video/tdav_converter_video.h"
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_string.h"
+#include "tsk_buffer.h"
+#include "tsk_time.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#include <libavcodec/avcodec.h>
+
+#define THEORA_RTP_PAYLOAD_SIZE 900
+#define THEORA_GOP_SIZE_IN_SECONDS 25
+#define THEORA_PAYLOAD_HEADER_SIZE 4 /* 2.2. Payload Header */
+#define THEORA_PAYLOAD_LENGTH_SIZE 2 /* 2.2. Payload Header */
+#define THEORA_IDENT_HEADER_SIZE 42 /* 6.2 Identification Header Decode */
+#define THEORA_CONF_SEND_COUNT 10 /* at 250ms, 500ms, 1000ms, .... */
+
+typedef struct tdav_codec_theora_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ struct{
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ // Encoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+ void* buffer;
+
+ uint64_t conf_last;
+ int conf_count;
+ tsk_bool_t force_idr;
+ int quality;
+ int rotation;
+ int32_t max_bw_kpbs;
+ } encoder;
+
+ // decoder
+ struct{
+ AVCodec* codec;
+ AVCodecContext* context;
+ AVFrame* picture;
+
+ tsk_bool_t opened;
+ uint8_t conf_ident[3];
+ tsk_buffer_t* conf_pkt;
+
+ void* accumulator;
+ uint8_t ebit;
+ tsk_size_t accumulator_pos;
+ uint16_t last_seq;
+ } decoder;
+}
+tdav_codec_theora_t;
+
+
+/* 2.2. Payload Header filed 'F'*/
+typedef enum theora_frag_type_e{
+ Not_Fragmented = 0,
+ Start_Fragment = 1,
+ Continuation_Fragment = 2,
+ End_Fragment = 3,
+}
+theora_frag_type_t;
+
+/* 2.2. Payload Header field 'TDT'*/
+typedef enum theora_datatype_e{
+ Raw_Theora_payload = 0,
+ Theora_Packed_Configuration_payload = 1,
+ Legacy_Theora_Comment_payload = 2,
+ Reserved = 3,
+}
+theora_datatype_t;
+
+static int tdav_codec_theora_open_encoder(tdav_codec_theora_t* self);
+static int tdav_codec_theora_open_decoder(tdav_codec_theora_t* self);
+static int tdav_codec_theora_close_encoder(tdav_codec_theora_t* self);
+static int tdav_codec_theora_close_decoder(tdav_codec_theora_t* self);
+
+static int tdav_codec_theora_send(tdav_codec_theora_t* self, const uint8_t* data, tsk_size_t size, theora_datatype_t tdt);
+static void tdav_codec_theora_rtp_callback(tdav_codec_theora_t *self, const void *data, tsk_size_t size, tsk_bool_t marker);
+
+static void tdav_codec_theora_encap(tdav_codec_theora_t* theora, const uint8_t* pdata, tsk_size_t size);
+
+/* ============ Theora Plugin interface functions ================= */
+
+static int tdav_codec_theora_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Codec not opened");
+ return -1;
+ }
+ if(param->value_type == tmedia_pvt_int32){
+ if(tsk_striequals(param->key, "action")){
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch(action){
+ case tmedia_codec_action_encode_idr:
+ {
+ theora->encoder.force_idr = tsk_true;
+ break;
+ }
+ case tmedia_codec_action_bw_down:
+ {
+ theora->encoder.quality = TSK_CLAMP(1, (theora->encoder.quality + 1), 31);
+ theora->encoder.context->global_quality = FF_QP2LAMBDA * theora->encoder.quality;
+ break;
+ }
+ case tmedia_codec_action_bw_up:
+ {
+ theora->encoder.quality = TSK_CLAMP(1, (theora->encoder.quality - 1), 31);
+ theora->encoder.context->global_quality = FF_QP2LAMBDA * theora->encoder.quality;
+ break;
+ }
+ }
+ }
+ // FIXME: not working as expected
+ /*else if(tsk_striequals(param->key, "rotation")){
+ int rotation = *((int32_t*)param->value);
+ if(theora->encoder.rotation != rotation){
+ if(self->opened){
+ int ret;
+ theora->encoder.rotation = rotation;
+ if((ret = tdav_codec_theora_close_encoder(theora))){
+ return ret;
+ }
+ if((ret = tdav_codec_theora_open_encoder(theora))){
+ return ret;
+ }
+ }
+ }
+ return 0;
+ }*/
+ }
+ return -1;
+}
+
+int tdav_codec_theora_open(tmedia_codec_t* self)
+{
+ int ret;
+
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+
+ if(!theora){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+
+ // Encoder
+ if((ret = tdav_codec_theora_open_encoder(theora))){
+ return ret;
+ }
+
+ // Decoder
+ if((ret = tdav_codec_theora_open_decoder(theora))){
+ return ret;
+ }
+
+ return 0;
+}
+
+int tdav_codec_theora_close(tmedia_codec_t* self)
+{
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+
+ if(!theora){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is opened */
+
+
+ // Encoder
+ tdav_codec_theora_close_encoder(theora);
+
+ // Decoder
+ tdav_codec_theora_close_decoder(theora);
+
+ return 0;
+}
+
+//#include "tsk_time.h"
+tsk_size_t tdav_codec_theora_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ int ret;
+ int size;
+
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+
+ if(!self || !in_data || !in_size || !out_data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ size = avpicture_fill((AVPicture *)theora->encoder.picture, (uint8_t*)in_data, PIX_FMT_YUV420P, theora->encoder.context->width, theora->encoder.context->height);
+ if(size != in_size){
+ /* guard */
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+
+ // Encode data
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ theora->encoder.picture->pict_type = theora->encoder.force_idr ? FF_I_TYPE : 0;
+#else
+ theora->encoder.picture->pict_type = theora->encoder.force_idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
+#endif
+ theora->encoder.picture->pts = AV_NOPTS_VALUE;
+ theora->encoder.picture->quality = theora->encoder.context->global_quality;
+ ret = avcodec_encode_video(theora->encoder.context, theora->encoder.buffer, size, theora->encoder.picture);
+ if(ret > 0){
+ tdav_codec_theora_encap(theora, theora->encoder.buffer, (tsk_size_t)ret);
+ }
+ theora->encoder.force_idr = tsk_false;
+
+ return 0;
+}
+
+tsk_size_t tdav_codec_theora_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)
+{
+ const uint8_t* pdata = in_data;
+ int pkts;
+ const uint8_t* pay_ptr;
+ tsk_size_t pay_size;
+ //tsk_size_t hdr_size;
+ tsk_size_t xsize, retsize = 0;
+ int got_picture_ptr;
+ int ret;
+
+ tdav_codec_theora_t* theora = (tdav_codec_theora_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+
+ if(!self || !in_data || (in_size<(THEORA_PAYLOAD_HEADER_SIZE + THEORA_PAYLOAD_LENGTH_SIZE)) || !out_data || !theora->decoder.context){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ /* Packet lost? */
+ if(theora->decoder.last_seq != (rtp_hdr->seq_num - 1) && theora->decoder.last_seq){
+ if(theora->decoder.last_seq == rtp_hdr->seq_num){
+ // Could happen on some stupid emulators
+ //TSK_DEBUG_INFO("Packet duplicated, seq_num=%d", rtp_hdr->seq_num);
+ return 0;
+ }
+ TSK_DEBUG_INFO("Packet lost, seq_num=%d", rtp_hdr->seq_num);
+ }
+ theora->decoder.last_seq = rtp_hdr->seq_num;
+
+ xsize = avpicture_get_size(theora->decoder.context->pix_fmt, theora->decoder.context->width, theora->decoder.context->height);
+
+ /* 2.2. Payload Header
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Configuration Ident | F |TDT|# pkts.|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ /* 2.3. Payload Data
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Payload Length | Theora Data ..
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ pkts = (pdata[3] & 0x0F);
+ pay_ptr = (pdata + THEORA_PAYLOAD_HEADER_SIZE);
+
+ do{ /* pkts=0 for fragmented packets */
+
+ pay_size = pay_ptr[0], pay_size<<=8, pay_size |= pay_ptr[1]; /* Big Endian read */
+ pay_ptr += THEORA_PAYLOAD_LENGTH_SIZE;
+ /* check size validity */
+ if((pay_ptr + pay_size)>(pdata + in_size)){
+ TSK_DEBUG_ERROR("Too short");
+ break;
+ }
+
+ switch((pdata[3]>>4) & 0x03){
+ case Raw_Theora_payload:
+ { /* ====== Theora data (2.2. Payload Header, 2.3. Payload Data) ====== */
+ /* append buffer */
+ if((int)(theora->decoder.accumulator_pos + pay_size) <= xsize){
+ memcpy(&((uint8_t*)theora->decoder.accumulator)[theora->decoder.accumulator_pos], pay_ptr, pay_size);
+ theora->decoder.accumulator_pos += pay_size;
+ }
+ else{
+ TSK_DEBUG_WARN("Buffer overflow");
+ theora->decoder.accumulator_pos = 0;
+ break;
+ }
+ /* only take care if last packet (What about the RTP marker?) */
+ if(((pdata[3]>>6) == Not_Fragmented || (pdata[3]>>6) == End_Fragment /*|| rtp_hdr->marker*/) && theora->decoder.opened){
+ AVPacket packet;
+ /* Perform decoding */
+ av_init_packet(&packet);
+ packet.size = (int)theora->decoder.accumulator_pos;
+ packet.data = theora->decoder.accumulator;
+ ret = avcodec_decode_video2(theora->decoder.context, theora->decoder.picture, &got_picture_ptr, &packet);
+
+ if(ret < 0){
+ TSK_DEBUG_WARN("Failed to decode the buffer with error code = %d", ret);
+ if(TMEDIA_CODEC_VIDEO(self)->in.callback){
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+ else if(got_picture_ptr){
+ retsize = xsize;
+ TMEDIA_CODEC_VIDEO(theora)->in.width = theora->decoder.context->width;
+ TMEDIA_CODEC_VIDEO(theora)->in.height = theora->decoder.context->height;
+
+ /* allocate buffer */
+ if(*out_max_size <xsize){
+ if((*out_data = tsk_realloc(*out_data, xsize))){
+ *out_max_size = xsize;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ return 0;
+ }
+ }
+ /* copy picture into a linear buffer */
+ avpicture_layout((AVPicture *)theora->decoder.picture, theora->decoder.context->pix_fmt, (int)theora->decoder.context->width, (int)theora->decoder.context->height,
+ *out_data, (int)retsize);
+ }
+ /* in all cases: reset accumulator */
+ theora->decoder.accumulator_pos = 0;
+ }
+ break;
+ }
+ case Theora_Packed_Configuration_payload:
+ {/* ====== Configuration packet (3.1.1. Packed Configuration) ====== */
+ static uint8_t __theora_comment_hdr[] = {0x81, 0x74, 0x68, 0x65, 0x6F, 0x72, 0x61,
+ 0x00, 0x00, 0x00, 0x08, /* 4-byte length */
+ 'd', 'o', 'u', 'b', 'a', 'n', 'g', 'o', /* UTF-8 encoded string */
+ };
+
+ /* http://www.theora.org/doc/Theora.pdf - Chapter 6
+ A Theora bitstream begins with three header packets. The header packets
+ are, in order, the identifcation header, the comment header, and the setup
+ header. All are required for decode compliance. An end-of-packet condition
+ encountered while decoding the identification or setup header packets renders
+ the stream undecodable. An end-of-packet condition encountered while decode
+ the comment header is a non-fatal error condition, and MAY be ignored by a
+ decoder.
+
+ Decode continues according to HEADERTYPE. The identification header
+ is type 0x80, the comment header is type 0x81, and the setup header is type
+ 0x82.
+ */
+ /*TSK_DEBUG_INFO("Theora_Packed_Configuration_payload");*/
+
+ if(!theora->decoder.opened /*|| (conf_ident changed)*/){
+ if(!theora->decoder.conf_pkt){
+ theora->decoder.conf_pkt = tsk_buffer_create(pay_ptr, pay_size);
+ }
+ else{
+ tsk_buffer_append(theora->decoder.conf_pkt, pay_ptr, pay_size);
+ }
+
+ if((pdata[3]>>6) == Not_Fragmented || (pdata[3]>>6) == End_Fragment || rtp_hdr->marker){
+ if(theora->decoder.conf_pkt->size > THEORA_IDENT_HEADER_SIZE){
+ const uint8_t* conf_ptr = theora->decoder.conf_pkt->data;
+ int setup_size = (int)theora->decoder.conf_pkt->size - THEORA_IDENT_HEADER_SIZE;
+ int extradata_size = (2 + THEORA_IDENT_HEADER_SIZE) + (2 + setup_size) + (2 + sizeof(__theora_comment_hdr));
+ if(conf_ptr[0] == 0x80 && conf_ptr[THEORA_IDENT_HEADER_SIZE] == 0x82){ /* Do not check for 't'h'e'o'r'a' */
+ /* save configration identification */
+ memcpy(theora->decoder.conf_ident, &pdata[0], sizeof(theora->decoder.conf_ident));
+ if(theora->decoder.context->extradata){
+ TSK_FREE(theora->decoder.context->extradata);
+ }
+ if((theora->decoder.context->extradata = tsk_calloc(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE, 1))){
+ int index = 0;
+ /* Because of endianess pb. do not use uint16_t or uint32_t */
+ theora->decoder.context->extradata[index++] = 0x00;
+ theora->decoder.context->extradata[index++] = THEORA_IDENT_HEADER_SIZE;
+ memcpy(&theora->decoder.context->extradata[index], &conf_ptr[0], THEORA_IDENT_HEADER_SIZE);
+ index += THEORA_IDENT_HEADER_SIZE;
+
+ theora->decoder.context->extradata[index++] = (setup_size >>8) & 0xFF;
+ theora->decoder.context->extradata[index++] = (setup_size & 0xFF);
+ memcpy(&theora->decoder.context->extradata[index], &conf_ptr[THEORA_IDENT_HEADER_SIZE], setup_size);
+ index+=setup_size;
+
+ theora->decoder.context->extradata[index++] = 0x00;
+ theora->decoder.context->extradata[index++] = sizeof(__theora_comment_hdr);/* <0xFF */
+ memcpy(&theora->decoder.context->extradata[index], __theora_comment_hdr, sizeof(__theora_comment_hdr));
+
+ theora->decoder.context->extradata_size = extradata_size;
+
+ if((ret = avcodec_open(theora->decoder.context, theora->decoder.codec)) == 0){
+ theora->decoder.opened = tsk_true;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to open theora decoder %d", ret);
+ TSK_FREE(theora->decoder.context->extradata);
+ theora->decoder.context->extradata_size = 0;
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Invalid configuration packet");
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Too short");
+ }
+ tsk_buffer_cleanup(theora->decoder.conf_pkt);
+ }
+ }
+ break;
+ }
+ case Legacy_Theora_Comment_payload:
+ /*TSK_DEBUG_INFO("Legacy_Theora_Comment_payload");*/
+ break;
+ case Reserved:
+ /*TSK_DEBUG_INFO("Reserved");*/
+ break;
+ }
+ }
+ while(--pkts>0);
+
+
+
+ return retsize;
+}
+
+tsk_bool_t tdav_codec_theora_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+ tsk_bool_t ret = tsk_true; // accept decoding any size
+
+ if(tsk_striequals(att_name, "fmtp")){
+ tsk_params_L_t* params;
+ if((params = tsk_params_fromstring(att_value, ";", tsk_true))){
+ int pref_width, pref_height;
+ int prop_width = tsk_params_get_param_value_as_int(params, "width");
+ int prop_height = tsk_params_get_param_value_as_int(params, "height");
+
+ if(prop_width > 0 && prop_height > 0){
+ if(tmedia_video_get_size(TMEDIA_CODEC_VIDEO(codec)->pref_size, (unsigned *)&pref_width, (unsigned *)&pref_height) != 0){
+ TSK_OBJECT_SAFE_FREE(params);
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = TMEDIA_CODEC_VIDEO(codec)->out.width = TSK_MIN(pref_width, prop_width);
+ TMEDIA_CODEC_VIDEO(codec)->in.height = TMEDIA_CODEC_VIDEO(codec)->out.height = TSK_MIN(pref_height, prop_height);
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+ }
+ }
+ else if(tsk_striequals(att_name, "imageattr")){
+ unsigned in_width, in_height, out_width, out_height;
+ if(tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0){
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = in_width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = in_height;
+ TMEDIA_CODEC_VIDEO(codec)->out.width = out_width;
+ TMEDIA_CODEC_VIDEO(codec)->out.height = out_height;
+ }
+
+ return ret;
+}
+
+char* tdav_codec_theora_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+ if(tsk_striequals(att_name, "fmtp")){
+ char* fmtp = tsk_null;
+ tsk_sprintf(&fmtp, "sampling=YCbCr-4:2:0; width=%u; height=%u", TMEDIA_CODEC_VIDEO(codec)->out.width, TMEDIA_CODEC_VIDEO(codec)->out.height);
+ return fmtp;
+ }
+ else if(tsk_striequals(att_name, "imageattr")){
+ return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(codec)->pref_size,
+ TMEDIA_CODEC_VIDEO(codec)->in.width, TMEDIA_CODEC_VIDEO(codec)->in.height, TMEDIA_CODEC_VIDEO(codec)->out.width, TMEDIA_CODEC_VIDEO(codec)->out.height);
+ }
+ return tsk_null;
+}
+
+
+
+/* constructor */
+static tsk_object_t* tdav_codec_theora_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_theora_t *theora = self;
+ if(theora){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ theora->encoder.quality = 1;
+ theora->encoder.max_bw_kpbs = tmedia_defaults_get_bandwidth_video_upload_max();
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_theora_dtor(tsk_object_t * self)
+{
+ tdav_codec_theora_t *theora = self;
+ if(theora){
+ /* deinit base */
+ tmedia_codec_video_deinit(self);
+ /* deinit self */
+ TSK_OBJECT_SAFE_FREE(theora->decoder.conf_pkt);
+ TSK_FREE(theora->rtp.ptr);
+ theora->rtp.size = 0;
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_theora_def_s =
+{
+ sizeof(tdav_codec_theora_t),
+ tdav_codec_theora_ctor,
+ tdav_codec_theora_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_theora_plugin_def_s =
+{
+ &tdav_codec_theora_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_theora,
+ "theora",
+ "Theora Codec",
+ TMEDIA_CODEC_FORMAT_THEORA,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (width, height, fps)*/
+ {176, 144, 0},// fps is @deprecated
+
+ tdav_codec_theora_set,
+ tdav_codec_theora_open,
+ tdav_codec_theora_close,
+ tdav_codec_theora_encode,
+ tdav_codec_theora_decode,
+ tdav_codec_theora_sdp_att_match,
+ tdav_codec_theora_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_theora_plugin_def_t = &tdav_codec_theora_plugin_def_s;
+
+
+
+int tdav_codec_theora_open_encoder(tdav_codec_theora_t* self)
+{
+ int ret, size;
+ int32_t max_bw_kpbs;
+ if(!self->encoder.codec && !(self->encoder.codec = avcodec_find_encoder(CODEC_ID_THEORA))){
+ TSK_DEBUG_ERROR("Failed to find Theora encoder");
+ return -1;
+ }
+ if(self->encoder.context){
+ TSK_DEBUG_ERROR("Encoder already initialized");
+ return -1;
+ }
+ self->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->encoder.context);
+
+ self->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->encoder.context->time_base.num = 1;
+ self->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(self)->out.fps;
+ self->encoder.context->width = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.context->height = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->encoder.context->mb_decision = FF_MB_DECISION_RD;
+
+ // Theoraenc doesn't honor 'CODEC_FLAG_QSCALE'
+ max_bw_kpbs = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
+ self->encoder.max_bw_kpbs
+ );
+ self->encoder.context->bit_rate = (max_bw_kpbs * 1024);// bps
+#if LIBAVCODEC_VERSION_MAJOR <= 53
+ self->encoder.context->rc_lookahead = 0;
+#endif
+ self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality;
+
+ self->encoder.context->thread_count = 0;
+ self->encoder.context->rtp_payload_size = THEORA_RTP_PAYLOAD_SIZE;
+ self->encoder.context->opaque = tsk_null;
+ self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->out.fps * THEORA_GOP_SIZE_IN_SECONDS);
+
+ // Picture (YUV 420)
+ if(!(self->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->encoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height);
+ if(!(self->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(self->encoder.context, self->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open Theora encoder");
+ return ret;
+ }
+
+ self->encoder.conf_last = 0;
+ self->encoder.conf_count = 0;
+
+ TSK_DEBUG_INFO("[THEORA] bitrate=%d bps", self->encoder.context->bit_rate);
+
+ return ret;
+}
+
+int tdav_codec_theora_open_decoder(tdav_codec_theora_t* self)
+{
+ int size;
+ if(!self->decoder.codec && !(self->decoder.codec = avcodec_find_decoder(CODEC_ID_THEORA))){
+ TSK_DEBUG_ERROR("Failed to find Theora decoder");
+ return -1;
+ }
+ if(self->decoder.context){
+ TSK_DEBUG_ERROR("Decoder already opened");
+ return -1;
+ }
+ self->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(self->decoder.context);
+
+ self->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ self->decoder.context->width = TMEDIA_CODEC_VIDEO(self)->in.width;
+ self->decoder.context->height = TMEDIA_CODEC_VIDEO(self)->in.height;
+
+ // Picture (YUV 420)
+ if(!(self->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(self->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, self->decoder.context->width, self->decoder.context->height);
+ if(!(self->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ if(!(self->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ // Open decoder
+ //if((ret = avcodec_open(self->decoder.context, self->decoder.codec)) < 0){
+ // TSK_DEBUG_ERROR("Failed to open Theora decoder");
+ // return ret;
+ //}
+
+ return 0;
+}
+
+int tdav_codec_theora_close_encoder(tdav_codec_theora_t* self)
+{
+ if(self->encoder.context){
+ avcodec_close(self->encoder.context);
+ av_free(self->encoder.context);
+ self->encoder.context = tsk_null;
+ }
+ if(self->encoder.picture){
+ av_free(self->encoder.picture);
+ self->encoder.picture = tsk_null;
+ }
+ if(self->encoder.buffer){
+ TSK_FREE(self->encoder.buffer);
+ }
+ return 0;
+}
+
+int tdav_codec_theora_close_decoder(tdav_codec_theora_t* self)
+{
+ if(self->decoder.context){
+ avcodec_close(self->decoder.context);
+ if(self->decoder.context->extradata){
+ TSK_FREE(self->decoder.context->extradata);
+ self->decoder.context->extradata_size = 0;
+ }
+ av_free(self->decoder.context);
+ self->decoder.context = tsk_null;
+ }
+ if(self->decoder.picture){
+ av_free(self->decoder.picture);
+ self->decoder.picture = tsk_null;
+ }
+ if(self->decoder.accumulator){
+ TSK_FREE(self->decoder.accumulator);
+ }
+ return 0;
+}
+
+static void tdav_codec_theora_encap(tdav_codec_theora_t* theora, const uint8_t* pdata, tsk_size_t size)
+{
+ if((theora->encoder.conf_count < THEORA_CONF_SEND_COUNT) && theora->encoder.context && theora->encoder.context->extradata){
+ if((theora->encoder.conf_last + (250 *theora->encoder.conf_count)) < tsk_time_now()){
+ int hdr_size, i, exd_size = theora->encoder.context->extradata_size, conf_pkt_size = 0;
+ uint8_t *conf_pkt_ptr = tsk_null, *exd_ptr = theora->encoder.context->extradata;
+ for(i=0; i<3 && exd_size; i++){
+ hdr_size = exd_ptr[0], hdr_size<<=8, hdr_size |= exd_ptr[1];
+ exd_ptr += 2;
+ exd_size -= 2;
+ if(hdr_size > exd_size){
+ TSK_DEBUG_ERROR("Invalid extradata");
+ TSK_FREE(conf_pkt_ptr);
+ conf_pkt_size = 0;
+ }
+
+ if(exd_ptr[0] == 0x80 || exd_ptr[0] == 0x82){ /* Ignore 'comment' which is equal to '0x81' */
+ if((conf_pkt_ptr = tsk_realloc(conf_pkt_ptr, (conf_pkt_size + hdr_size)))){
+ memcpy((conf_pkt_ptr + conf_pkt_size), exd_ptr, hdr_size);
+ conf_pkt_size += hdr_size;
+ }
+ }
+ exd_size -= hdr_size;
+ exd_ptr += hdr_size;
+ }
+
+ /* Send the conf pack */
+ if(conf_pkt_ptr && conf_pkt_size){
+ /*TSK_DEBUG_INFO("Sending Configuration Packet");*/
+ tdav_codec_theora_send(theora, conf_pkt_ptr, conf_pkt_size, Theora_Packed_Configuration_payload);
+ TSK_FREE(conf_pkt_ptr);
+ }
+
+ theora->encoder.conf_last = tsk_time_now();
+ theora->encoder.conf_count++;
+ }
+ }
+
+ /* Send Theora Raw data */
+ tdav_codec_theora_send(theora, pdata, size, Raw_Theora_payload);
+}
+
+int tdav_codec_theora_send(tdav_codec_theora_t* self, const uint8_t* data, tsk_size_t size, theora_datatype_t tdt)
+{
+ /* 2.2. Payload Header
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Configuration Ident | F |TDT|# pkts.|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ uint8_t pay_hdr[THEORA_PAYLOAD_HEADER_SIZE/*4*/ + THEORA_PAYLOAD_LENGTH_SIZE/*2*/] = {0x01, 0x19, 0x83, 0x00, 0x00, 0x00};
+ //uint8_t* pay_ptr = tsk_null;
+ tsk_size_t pay_size;
+ tsk_bool_t frag, first = tsk_true;
+
+ pay_hdr[3] = (tdt & 0xFF) <<4;
+
+ /* whether the packet will be fragmented or not */
+ frag = (size > THEORA_RTP_PAYLOAD_SIZE);
+
+ while(size){
+ pay_size = TSK_MIN(THEORA_RTP_PAYLOAD_SIZE, size);
+ pay_hdr[4] = (uint8_t)(pay_size >> 8), pay_hdr[5] = (uint8_t)(pay_size & 0xFF);
+
+ if(frag){
+ if(first){
+ first = tsk_false;
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (Start_Fragment <<6);
+ }
+ else{ /* could not be 'first' and 'last' */
+ if(size<=THEORA_RTP_PAYLOAD_SIZE){
+ /* Last frag */
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (End_Fragment <<6);
+ }
+ else{
+ /* Continuation frag */
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (Continuation_Fragment <<6);
+ }
+ }
+ }
+ else{
+ pay_hdr[3] |= 0x01; /* 'pkts' */
+ pay_hdr[3] &= 0x3F, pay_hdr[3] |= (Not_Fragmented <<6);
+ }
+
+ if(self->rtp.size < (pay_size + sizeof(pay_hdr))){
+ if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (pay_size + sizeof(pay_hdr))))){
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return -2;
+ }
+ self->rtp.size = (pay_size + sizeof(pay_hdr));
+ }
+
+ memcpy(self->rtp.ptr, pay_hdr, sizeof(pay_hdr));
+ memcpy((self->rtp.ptr + sizeof(pay_hdr)), data, pay_size);
+ data += pay_size;
+ size -= pay_size;
+
+ // Send data over the network
+ if(TMEDIA_CODEC_VIDEO(self)->out.callback){
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->rtp.ptr;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (pay_size + sizeof(pay_hdr));
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (1./(double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate;
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = (size == 0);
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
+ }
+
+ return 0;
+}
+
+tsk_bool_t tdav_codec_ffmpeg_theora_is_supported()
+{
+ return (avcodec_find_encoder(CODEC_ID_THEORA) && avcodec_find_decoder(CODEC_ID_THEORA));
+}
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
diff --git a/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c b/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c
new file mode 100644
index 0000000..2c097e4
--- /dev/null
+++ b/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c
@@ -0,0 +1,1059 @@
+/*
+* Copyright (C) 2011-2015 Doubango Telecom <http://www.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_vp8.c
+* @brief VP8 codec
+* The RTP packetizer/depacketizer follows draft-ietf-payload-vp8 and draft-bankoski-vp8-bitstream-05
+* Google's VP8 (http://www.webmproject.org/) encoder/decoder
+*
+* We require v1.3.0 (2013-12-02 10:37:51) or later. For iOS, because of issue 423 (https://code.google.com/p/doubango/issues/detail?id=423) we require a version after "Mon, 28 Apr 2014 22:42:23 +0100 (14:42 -0700)" integrating fix in http://git.chromium.org/gitweb/?p=webm/libvpx.git;a=commit;h=33df6d1fc1d268b4901b74b4141f83594266f041
+*
+*/
+#include "tinydav/codecs/vpx/tdav_codec_vp8.h"
+
+#if HAVE_LIBVPX
+
+#if TDAV_UNDER_WINDOWS
+# include <windows.h>
+#endif
+
+#include "tinyrtp/rtp/trtp_rtp_packet.h"
+
+#include "tinymedia/tmedia_params.h"
+#include "tinymedia/tmedia_defaults.h"
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_time.h"
+#include "tsk_debug.h"
+
+#define VPX_CODEC_DISABLE_COMPAT 1 /* strict compliance with the latest SDK by disabling some backwards compatibility */
+#include <vpx/vpx_encoder.h>
+#include <vpx/vpx_decoder.h>
+#include <vpx/vp8cx.h>
+#include <vpx/vp8dx.h>
+
+#if !defined(TDAV_VP8_DISABLE_EXTENSION)
+# define TDAV_VP8_DISABLE_EXTENSION 0 /* Set X fied value to zero */
+#endif
+
+#if TDAV_VP8_DISABLE_EXTENSION
+# define TDAV_VP8_PAY_DESC_SIZE 1
+#else
+# define TDAV_VP8_PAY_DESC_SIZE 4
+#endif
+#define TDAV_SYSTEM_CORES_COUNT 0
+#define TDAV_VP8_GOP_SIZE_IN_SECONDS 60
+#define TDAV_VP8_RTP_PAYLOAD_MAX_SIZE 1050
+#if !defined(TDAV_VP8_MAX_BANDWIDTH_KB)
+# define TDAV_VP8_MAX_BANDWIDTH_KB 6000
+#endif
+#if !defined(TDAV_VP8_MIN_BANDWIDTH_KB)
+# define TDAV_VP8_MIN_BANDWIDTH_KB 100
+#endif
+
+/* VP8 codec */
+typedef struct tdav_codec_vp8_s
+{
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ // Encoder
+ struct {
+ vpx_codec_enc_cfg_t cfg;
+ tsk_bool_t initialized;
+ vpx_codec_pts_t pts;
+ vpx_codec_ctx_t context;
+ unsigned pic_id : 15;
+ uint64_t frame_count;
+ tsk_bool_t force_idr;
+ int rotation;
+
+ struct {
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ tsk_mutex_handle_t* mutex;
+ } encoder;
+
+ // decoder
+ struct {
+ vpx_codec_dec_cfg_t cfg;
+ unsigned initialized : 1;
+ vpx_codec_ctx_t context;
+ void* accumulator;
+ tsk_size_t accumulator_pos;
+ tsk_size_t accumulator_size;
+ tsk_size_t first_part_size;
+ uint16_t last_seq;
+ uint32_t last_timestamp;
+ tsk_bool_t idr;
+ tsk_bool_t corrupted;
+ } decoder;
+}
+tdav_codec_vp8_t;
+
+#define vp8_interface_enc (vpx_codec_vp8_cx())
+#define vp8_interface_dec (vpx_codec_vp8_dx())
+
+static int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self);
+static int tdav_codec_vp8_open_decoder(tdav_codec_vp8_t* self);
+static int tdav_codec_vp8_close_encoder(tdav_codec_vp8_t* self);
+static int tdav_codec_vp8_close_decoder(tdav_codec_vp8_t* self);
+
+static void tdav_codec_vp8_encap(tdav_codec_vp8_t* self, const vpx_codec_cx_pkt_t *pkt);
+static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data, tsk_size_t size, uint32_t partID, tsk_bool_t part_start, tsk_bool_t non_ref, tsk_bool_t last);
+
+/* ============ VP8 Plugin interface ================= */
+
+static int tdav_codec_vp8_set(tmedia_codec_t* self, const tmedia_param_t* param)
+{
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
+ tsk_bool_t reconf = tsk_false;
+
+ if (param->value_type == tmedia_pvt_int32) {
+ if (tsk_striequals(param->key, "action")) {
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch (action) {
+ case tmedia_codec_action_encode_idr:
+ {
+ vp8->encoder.force_idr = tsk_true;
+ return 0;
+ }
+ case tmedia_codec_action_bw_down:
+ {
+ vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (int32_t)((vp8->encoder.cfg.rc_target_bitrate << 1) / 3), TMEDIA_CODEC(vp8)->bandwidth_max_upload);
+ TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
+ reconf = tsk_true;
+ break;
+ }
+ case tmedia_codec_action_bw_up:
+ {
+ vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (int32_t)((vp8->encoder.cfg.rc_target_bitrate * 3) >> 1), TMEDIA_CODEC(vp8)->bandwidth_max_upload);
+ TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
+ reconf = tsk_true;
+ break;
+ }
+ }
+ }
+ else if (tsk_striequals(param->key, "bw_kbps")) { // both up and down (from the SDP)
+ int32_t max_bw_userdefine = tmedia_defaults_get_bandwidth_video_upload_max();
+ int32_t max_bw_new = *((int32_t*)param->value);
+ if (max_bw_userdefine > 0) {
+ // do not use more than what the user defined in it's configuration
+ TMEDIA_CODEC(vp8)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine);
+ }
+ else {
+ TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new;
+ }
+ vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, vp8->encoder.cfg.rc_target_bitrate, TMEDIA_CODEC(vp8)->bandwidth_max_upload);
+ TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
+ reconf = tsk_true;
+ }
+ else if (tsk_striequals(param->key, "bandwidth-max-upload")) {
+ int32_t bw_max_upload = *((int32_t*)param->value);
+ TSK_DEBUG_INFO("VP8 codec: bandwidth-max-upload=%d", bw_max_upload);
+ TMEDIA_CODEC(vp8)->bandwidth_max_upload = bw_max_upload;
+ reconf = tsk_true;
+ }
+ else if (tsk_striequals(param->key, "rotation")) {
+ // IMPORTANT: changing resolution requires at least libvpx v1.1.0 "Eider"
+ int32_t rotation = *((int32_t*)param->value);
+ if (vp8->encoder.rotation != rotation) {
+ vp8->encoder.rotation = rotation;
+ vp8->encoder.cfg.g_w = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(vp8)->out.height : TMEDIA_CODEC_VIDEO(vp8)->out.width;
+ vp8->encoder.cfg.g_h = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(vp8)->out.width : TMEDIA_CODEC_VIDEO(vp8)->out.height;
+ reconf = tsk_true;
+ }
+ }
+ }
+
+ if (reconf) {
+ if (vp8->encoder.initialized) {
+ // The encoder isn't thread safe. Without this lock (and the one in the encode() function) we may have corruptions in the video (issue report from GE).
+ // Google says the encoder is thread-safe but this is not the case. But it is *multi-instance* thread-safe.
+ tsk_mutex_lock(vp8->encoder.mutex);
+ if ((vpx_ret = vpx_codec_enc_config_set(&vp8->encoder.context, &vp8->encoder.cfg)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_enc_config_set failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ }
+ tsk_mutex_unlock(vp8->encoder.mutex);
+ }
+ return (vpx_ret == VPX_CODEC_OK) ? 0 : -2;
+ }
+
+ return -1;
+}
+
+static int tdav_codec_vp8_open(tmedia_codec_t* self)
+{
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ int ret;
+
+ if (!vp8) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* the caller (base class) already checked that the codec is not opened */
+
+
+ // Encoder
+ if ((ret = tdav_codec_vp8_open_encoder(vp8))) {
+ return ret;
+ }
+
+ // Decoder
+ if ((ret = tdav_codec_vp8_open_decoder(vp8))) {
+ return ret;
+ }
+
+ return ret;
+}
+
+static int tdav_codec_vp8_close(tmedia_codec_t* self)
+{
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+
+ if (!vp8) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tdav_codec_vp8_close_encoder(vp8);
+ tdav_codec_vp8_close_decoder(vp8);
+
+ return 0;
+}
+
+static tsk_size_t tdav_codec_vp8_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_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ vpx_enc_frame_flags_t flags = 0;
+ vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
+ const vpx_codec_cx_pkt_t *pkt;
+ vpx_codec_iter_t iter = tsk_null;
+ vpx_image_t image = {0};
+
+ if (!vp8 || !in_data || !in_size) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if (in_size != (vp8->encoder.context.config.enc->g_w * vp8->encoder.context.config.enc->g_h * 3) >> 1) {
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ if (!vpx_img_wrap(&image, VPX_IMG_FMT_I420, vp8->encoder.context.config.enc->g_w, vp8->encoder.context.config.enc->g_h, 1, (unsigned char*)in_data)) {
+ TSK_DEBUG_ERROR("vpx_img_wrap failed");
+ return 0;
+ }
+
+ // encode data
+ ++vp8->encoder.pts;
+ if (vp8->encoder.force_idr) {
+ flags |= VPX_EFLAG_FORCE_KF;
+ vp8->encoder.force_idr = tsk_false;
+ }
+ tsk_mutex_lock(vp8->encoder.mutex); // must
+ vpx_ret = vpx_codec_encode(&vp8->encoder.context, &image, vp8->encoder.pts, 1, flags, VPX_DL_REALTIME);
+ tsk_mutex_unlock(vp8->encoder.mutex);
+
+ if (vpx_ret != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_encode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ goto bail;
+ }
+
+ ++vp8->encoder.frame_count;
+ ++vp8->encoder.pic_id;
+
+ while ((pkt = vpx_codec_get_cx_data(&vp8->encoder.context, &iter))) {
+ switch (pkt->kind) {
+ case VPX_CODEC_CX_FRAME_PKT:
+ {
+ tdav_codec_vp8_encap(vp8, pkt);
+ break;
+ }
+ default:
+ case VPX_CODEC_STATS_PKT: /**< Two-pass statistics for this frame */
+ case VPX_CODEC_PSNR_PKT: /**< PSNR statistics for this frame */
+ case VPX_CODEC_CUSTOM_PKT: /**< Algorithm extensions */
+ {
+ TSK_DEBUG_INFO("pkt->kind=%d not supported", (int)pkt->kind);
+ break;
+ }
+ }
+ }
+
+bail:
+ vpx_img_free(&image);
+ return 0;
+}
+
+static tsk_size_t tdav_codec_vp8_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_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+ const uint8_t* pdata = in_data;
+ const uint8_t* pdata_end = (pdata + in_size);
+ tsk_size_t ret = 0;
+ tsk_bool_t fatal_error = tsk_false;
+ static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
+ uint8_t S, PartID;
+
+ if (!self || !in_data || in_size < 1 || !out_data || !vp8->decoder.initialized) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ { /* 4.2. VP8 Payload Descriptor */
+ uint8_t X, R, N, I, L, T, K;//TODO: store
+
+ X = ((*pdata & 0x80) >> 7);
+ R = ((*pdata & 0x40) >> 6);
+ if (R) {
+ TSK_DEBUG_ERROR("R<>0");
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ N = ((*pdata & 0x20) >> 5);
+ S = ((*pdata & 0x10) >> 4);
+ PartID = (*pdata & 0x0F);
+ // skip "REQUIRED" header
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short"); goto bail;
+ }
+ // check "OPTIONAL" headers
+ if (X) {
+ I = (*pdata & 0x80);
+ L = (*pdata & 0x40);
+ T = (*pdata & 0x20);
+ K = (*pdata & 0x10);
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short"); goto bail;
+ }
+
+ if (I) {
+ if (*pdata & 0x80) { // M
+ // PictureID on 16bits
+ if ((pdata += 2) >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short"); goto bail;
+ }
+ }
+ else {
+ // PictureID on 8bits
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short"); goto bail;
+ }
+ }
+ }
+ if (L) {
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short"); goto bail;
+ }
+ }
+ if (T || K) {
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short"); goto bail;
+ }
+ }
+ }
+ }
+
+ in_size = (pdata_end - pdata);
+
+ // Packet lost?
+ if (vp8->decoder.last_seq && (vp8->decoder.last_seq + 1) != rtp_hdr->seq_num) {
+ TSK_DEBUG_INFO("[VP8] Packet loss, seq_num=%d", (vp8->decoder.last_seq + 1));
+ vp8->decoder.corrupted = tsk_true;
+ }
+ vp8->decoder.last_seq = rtp_hdr->seq_num;
+
+ // New frame ?
+ if (vp8->decoder.last_timestamp != rtp_hdr->timestamp) {
+ /* 4.3. VP8 Payload Header
+ Note that the header is present only in packets
+ which have the S bit equal to one and the PartID equal to zero in the
+ payload descriptor. Subsequent packets for the same frame do not
+ carry the payload header.
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |Size0|H| VER |P|
+ +-+-+-+-+-+-+-+-+
+ | Size1 |
+ +-+-+-+-+-+-+-+-+
+ | Size2 |
+ +-+-+-+-+-+-+-+-+
+ | Bytes 4..N of |
+ | VP8 payload |
+ : :
+ +-+-+-+-+-+-+-+-+
+ | OPTIONAL RTP |
+ | padding |
+ : :
+ +-+-+-+-+-+-+-+-+
+ P: Inverse key frame flag. When set to 0 the current frame is a key
+ frame. When set to 1 the current frame is an interframe. Defined
+ in [RFC6386]
+ */
+
+ // Reset accumulator position
+ vp8->decoder.accumulator_pos = 0;
+
+ // Make sure the header is present
+ if (S != 1 || PartID != 0 || in_size < 3) {
+ TSK_DEBUG_WARN("VP8 payload header is missing");
+#if 0
+ if (in_size < 3)
+#endif
+ {
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ }
+ {
+ /* SizeN: The size of the first partition in bytes is calculated from
+ the 19 bits in Size0, Size1, and Size2 as 1stPartitionSize = Size0
+ + 8 * Size1 + 2048 * Size2. [RFC6386]. */
+ vp8->decoder.first_part_size = ((pdata[0] >> 5) & 0xFF) + 8 * pdata[1] + 2048 * pdata[2];
+ }
+
+ // Starting new frame...reset "corrupted" value
+ vp8->decoder.corrupted = tsk_false;
+
+ // Key frame?
+ vp8->decoder.idr = !(pdata[0] & 0x01);
+
+ // Update timestamp
+ vp8->decoder.last_timestamp = rtp_hdr->timestamp;
+ }
+
+ if (in_size > xmax_size) {
+ vp8->decoder.accumulator_pos = 0;
+ TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", (unsigned)in_size, (unsigned)xmax_size);
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ // start-accumulator
+ if (!vp8->decoder.accumulator) {
+ if (!(vp8->decoder.accumulator = tsk_calloc(in_size, sizeof(uint8_t)))) {
+ TSK_DEBUG_ERROR("Failed to allocated new buffer");
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ vp8->decoder.accumulator_size = in_size;
+ }
+ if ((vp8->decoder.accumulator_pos + in_size) >= xmax_size) {
+ TSK_DEBUG_ERROR("BufferOverflow");
+ vp8->decoder.accumulator_pos = 0;
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ if ((vp8->decoder.accumulator_pos + in_size) > vp8->decoder.accumulator_size) {
+ if (!(vp8->decoder.accumulator = tsk_realloc(vp8->decoder.accumulator, (vp8->decoder.accumulator_pos + in_size)))) {
+ TSK_DEBUG_ERROR("Failed to reallocated new buffer");
+ vp8->decoder.accumulator_pos = 0;
+ vp8->decoder.accumulator_size = 0;
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ vp8->decoder.accumulator_size = (vp8->decoder.accumulator_pos + in_size);
+ }
+
+ memcpy(&((uint8_t*)vp8->decoder.accumulator)[vp8->decoder.accumulator_pos], pdata, in_size);
+ vp8->decoder.accumulator_pos += in_size;
+ // end-accumulator
+
+ // Decode the frame if we have a marker or the first partition is complete and not corrupted
+ if (rtp_hdr->marker /*|| (!vp8->decoder.corrupted && vp8->decoder.first_part_size == vp8->decoder.accumulator_pos)*/) {
+ vpx_image_t *img;
+ vpx_codec_iter_t iter = tsk_null;
+ vpx_codec_err_t vpx_ret;
+ const uint8_t* pay_ptr = (const uint8_t*)vp8->decoder.accumulator;
+ const tsk_size_t pay_size = vp8->decoder.accumulator_pos;
+
+ // in all cases: reset accumulator position
+ vp8->decoder.accumulator_pos = 0;
+
+#if 0 /* http://groups.google.com/a/webmproject.org/group/apps-devel/browse_thread/thread/c84438e70fe122fa/2dfc322018aa22a8 */
+ // libvpx will crash very ofen when the frame is corrupted => for now we decided not to decode such frame
+ // according to the latest release there is a function to check if the frame
+ // is corrupted or not => To be checked
+ if(vp8->decoder.corrupted) {
+ vp8->decoder.corrupted = tsk_false;
+ goto bail;
+ }
+#endif
+
+ if (pay_size < vp8->decoder.first_part_size) {
+ TSK_DEBUG_WARN("[VP8] No enough bytes for the first part: %u < %u", (unsigned)pay_size, (unsigned)vp8->decoder.first_part_size);
+ // Not a fatal error
+ goto bail;
+ }
+
+ vpx_ret = vpx_codec_decode(&vp8->decoder.context, pay_ptr, (int)pay_size, tsk_null, 0);
+
+ if (vpx_ret != VPX_CODEC_OK) {
+ TSK_DEBUG_INFO("vpx_codec_decode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ else if (vp8->decoder.idr) {
+ TSK_DEBUG_INFO("Decoded VP8 IDR");
+ if (TMEDIA_CODEC_VIDEO(self)->in.callback) {
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+
+ // copy decoded data
+ ret = 0;
+ while ((img = vpx_codec_get_frame(&vp8->decoder.context, &iter))) {
+ unsigned int plane, y;
+ tsk_size_t xsize;
+
+ // update sizes
+ TMEDIA_CODEC_VIDEO(vp8)->in.width = img->d_w;
+ TMEDIA_CODEC_VIDEO(vp8)->in.height = img->d_h;
+ xsize = (TMEDIA_CODEC_VIDEO(vp8)->in.width * TMEDIA_CODEC_VIDEO(vp8)->in.height * 3) >> 1;
+ // allocate destination buffer
+ if (*out_max_size < xsize) {
+ if (!(*out_data = tsk_realloc(*out_data, xsize))) {
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ goto bail;
+ }
+ *out_max_size = xsize;
+ }
+
+ // layout picture
+ for (plane = 0; plane < 3; plane++) {
+ unsigned char *buf = img->planes[plane];
+ for (y = 0; y < img->d_h >> (plane ? 1 : 0); y++) {
+ unsigned int w_count = img->d_w >> (plane ? 1 : 0);
+ if ((ret + w_count) > *out_max_size) {
+ TSK_DEBUG_ERROR("BufferOverflow");
+ ret = 0;
+ goto bail;
+ }
+ memcpy(((uint8_t*)*out_data) + ret, buf, w_count);
+ ret += w_count;
+ buf += img->stride[plane];
+ }
+ }
+ }
+ }
+
+bail:
+ if (fatal_error && TMEDIA_CODEC_VIDEO(self)->in.callback) {
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+
+ // vp8->decoder.last_PartID = PartID;
+ // vp8->decoder.last_S = S;
+ // vp8->decoder.last_N = N;
+ return ret;
+}
+
+static tsk_bool_t tdav_codec_vp8_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
+{
+#if 0
+ if(tsk_striequals(att_name, "fmtp")) {
+ unsigned width, height, fps;
+ if(tmedia_parse_video_fmtp(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &width, &height, &fps)) {
+ TSK_DEBUG_ERROR("Failed to match fmtp=%s", att_value);
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = TMEDIA_CODEC_VIDEO(codec)->out.width = width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = TMEDIA_CODEC_VIDEO(codec)->out.height = height;
+ TMEDIA_CODEC_VIDEO(codec)->in.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps = fps;
+ }
+ else
+#endif
+ if (tsk_striequals(att_name, "imageattr")) {
+ unsigned in_width, in_height, out_width, out_height;
+ if (tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0) {
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = in_width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = in_height;
+ TMEDIA_CODEC_VIDEO(codec)->out.width = out_width;
+ TMEDIA_CODEC_VIDEO(codec)->out.height = out_height;
+ }
+
+ return tsk_true;
+}
+
+static char* tdav_codec_vp8_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
+{
+#if 0
+ if(tsk_striequals(att_name, "fmtp")) {
+ return tmedia_get_video_fmtp(TMEDIA_CODEC_VIDEO(codec)->pref_size);
+ }
+ else
+#endif
+ if (tsk_striequals(att_name, "imageattr")) {
+ return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(codec)->pref_size,
+ TMEDIA_CODEC_VIDEO(codec)->in.width, TMEDIA_CODEC_VIDEO(codec)->in.height, TMEDIA_CODEC_VIDEO(codec)->out.width, TMEDIA_CODEC_VIDEO(codec)->out.height);
+ }
+ return tsk_null;
+}
+
+/* ============ VP8 object definition ================= */
+
+/* constructor */
+static tsk_object_t* tdav_codec_vp8_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_codec_vp8_t *vp8 = self;
+ if (vp8) {
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_codec_vp8_dtor(tsk_object_t * self)
+{
+ tdav_codec_vp8_t *vp8 = self;
+ TSK_DEBUG_INFO("*** tdav_codec_vp8_dtor destroyed ***");
+ if (vp8) {
+ /* deinit base */
+ tmedia_codec_video_deinit(vp8);
+ /* deinit self */
+ tdav_codec_vp8_close_encoder(vp8);
+ tdav_codec_vp8_close_decoder(vp8);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_codec_vp8_def_s =
+{
+ sizeof(tdav_codec_vp8_t),
+ tdav_codec_vp8_ctor,
+ tdav_codec_vp8_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tdav_codec_vp8_plugin_def_s =
+{
+ &tdav_codec_vp8_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_vp8,
+ "VP8",
+ "VP8 codec (libvpx)",
+ TMEDIA_CODEC_FORMAT_VP8,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (defaul width,height,fps) */
+ { 176, 144, 0 }, // fps is @deprecated
+
+ tdav_codec_vp8_set,
+ tdav_codec_vp8_open,
+ tdav_codec_vp8_close,
+ tdav_codec_vp8_encode,
+ tdav_codec_vp8_decode,
+ tdav_codec_vp8_sdp_att_match,
+ tdav_codec_vp8_sdp_att_get
+};
+const tmedia_codec_plugin_def_t *tdav_codec_vp8_plugin_def_t = &tdav_codec_vp8_plugin_def_s;
+
+/* ============ Internal functions ================= */
+
+int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
+{
+ vpx_codec_err_t vpx_ret;
+ vpx_enc_frame_flags_t enc_flags = 0; // VPX_EFLAG_XXX
+
+ if (self->encoder.initialized) {
+ TSK_DEBUG_ERROR("VP8 encoder already inialized");
+ return -1;
+ }
+
+ if ((vpx_ret = vpx_codec_enc_config_default(vp8_interface_enc, &self->encoder.cfg, 0)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_enc_config_default failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ return -2;
+ }
+ self->encoder.cfg.g_timebase.num = 1;
+ self->encoder.cfg.g_timebase.den = TMEDIA_CODEC_VIDEO(self)->out.fps;
+ self->encoder.cfg.rc_target_bitrate = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
+ TMEDIA_CODEC(self)->bandwidth_max_upload
+ );
+ self->encoder.cfg.g_w = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.cfg.g_h = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->encoder.cfg.kf_mode = VPX_KF_AUTO;
+ /*self->encoder.cfg.kf_min_dist =*/ self->encoder.cfg.kf_max_dist = (TDAV_VP8_GOP_SIZE_IN_SECONDS * TMEDIA_CODEC_VIDEO(self)->out.fps);
+#if defined(VPX_ERROR_RESILIENT_DEFAULT)
+ self->encoder.cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT;
+#else
+ self->encoder.cfg.g_error_resilient = 1;
+#endif
+#if defined(VPX_ERROR_RESILIENT_PARTITIONS)
+ self->encoder.cfg.g_error_resilient |= VPX_ERROR_RESILIENT_PARTITIONS;
+#endif
+#if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
+ enc_flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
+#endif
+ self->encoder.cfg.g_lag_in_frames = 0;
+#if TDAV_UNDER_WINDOWS
+ {
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ self->encoder.cfg.g_threads = SystemInfo.dwNumberOfProcessors;
+ }
+#endif
+ self->encoder.cfg.rc_end_usage = VPX_CBR;
+ self->encoder.cfg.g_pass = VPX_RC_ONE_PASS;
+#if 0
+ self->encoder.cfg.rc_dropframe_thresh = 30;
+ self->encoder.cfg.rc_resize_allowed = 0;
+ self->encoder.cfg.rc_min_quantizer = 2;
+ self->encoder.cfg.rc_max_quantizer = 56;
+ self->encoder.cfg.rc_undershoot_pct = 100;
+ self->encoder.cfg.rc_overshoot_pct = 15;
+ self->encoder.cfg.rc_buf_initial_sz = 500;
+ self->encoder.cfg.rc_buf_optimal_sz = 600;
+ self->encoder.cfg.rc_buf_sz = 1000;
+#endif
+
+ if ((vpx_ret = vpx_codec_enc_init(&self->encoder.context, vp8_interface_enc, &self->encoder.cfg, enc_flags)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_enc_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ return -3;
+ }
+ self->encoder.pic_id = /*(rand() ^ rand()) % 0x7FFF*/0/*Use zero: why do you want to make your life harder?*/;
+
+ /* vpx_codec_control(&self->encoder.context, VP8E_SET_STATIC_THRESHOLD, 800); */
+#if !TDAV_UNDER_MOBILE /* must not remove: crash on Android for sure and probably on iOS also (all ARM devices ?) */
+ vpx_codec_control(&self->encoder.context, VP8E_SET_NOISE_SENSITIVITY, 2);
+#elif TDAV_UNDER_WINDOWS_CE
+ vpx_codec_control(&self->encoder.context, VP8E_SET_NOISE_SENSITIVITY, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_CPUUSED, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_STATIC_THRESHOLD, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_SHARPNESS, 16);
+#endif
+
+ // Set number of partitions
+#if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
+ {
+ unsigned _s = TMEDIA_CODEC_VIDEO(self)->out.height * TMEDIA_CODEC_VIDEO(self)->out.width;
+ if (_s < (352 * 288)) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION);
+ }
+ else if (_s < (352 * 288) * 2 * 2) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_TWO_TOKENPARTITION);
+ }
+ else if (_s < (352 * 288) * 4 * 4) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_FOUR_TOKENPARTITION);
+ }
+ else if (_s < (352 * 288) * 8 * 8) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_EIGHT_TOKENPARTITION);
+ }
+ }
+#endif
+
+ // Create the mutex if not already done
+ if (!self->encoder.mutex && !(self->encoder.mutex = tsk_mutex_create())) {
+ vpx_codec_destroy(&self->encoder.context);
+ TSK_DEBUG_ERROR("Failed to create mutex");
+ return -4;
+ }
+
+ self->encoder.frame_count = 0;
+
+ self->encoder.initialized = tsk_true;
+
+ TSK_DEBUG_INFO("[VP8] target_bitrate=%d kbps", self->encoder.cfg.rc_target_bitrate);
+
+ return 0;
+}
+
+int tdav_codec_vp8_open_decoder(tdav_codec_vp8_t* self)
+{
+ vpx_codec_err_t vpx_ret;
+ vpx_codec_caps_t dec_caps;
+ vpx_codec_flags_t dec_flags = 0;
+#if !TDAV_UNDER_MOBILE
+ static vp8_postproc_cfg_t __pp = { VP8_DEBLOCK | VP8_DEMACROBLOCK, 4, 0 };
+#endif
+
+ if (self->decoder.initialized) {
+ TSK_DEBUG_ERROR("VP8 decoder already initialized");
+ return -1;
+ }
+
+ self->decoder.cfg.w = TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->decoder.cfg.h = TMEDIA_CODEC_VIDEO(self)->out.height;
+#if TDAV_UNDER_WINDOWS
+ {
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ self->decoder.cfg.threads = SystemInfo.dwNumberOfProcessors;
+ }
+#endif
+
+ dec_caps = vpx_codec_get_caps(&vpx_codec_vp8_dx_algo);
+#if !TDAV_UNDER_MOBILE
+ if (dec_caps & VPX_CODEC_CAP_POSTPROC) {
+ dec_flags |= VPX_CODEC_USE_POSTPROC;
+ }
+#endif
+#if defined(VPX_CODEC_CAP_ERROR_CONCEALMENT)
+ if (dec_caps & VPX_CODEC_CAP_ERROR_CONCEALMENT) {
+ dec_flags |= VPX_CODEC_USE_ERROR_CONCEALMENT;
+ }
+#endif
+
+ if ((vpx_ret = vpx_codec_dec_init(&self->decoder.context, vp8_interface_dec, &self->decoder.cfg, dec_flags)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ return -4;
+ }
+#if !TDAV_UNDER_MOBILE
+ if ((vpx_ret = vpx_codec_control(&self->decoder.context, VP8_SET_POSTPROC, &__pp))) {
+ TSK_DEBUG_WARN("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ }
+#endif
+ self->decoder.initialized = tsk_true;
+
+ return 0;
+}
+
+int tdav_codec_vp8_close_encoder(tdav_codec_vp8_t* self)
+{
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(begin)");
+ if (self->encoder.initialized) {
+ vpx_codec_destroy(&self->encoder.context);
+ self->encoder.initialized = tsk_false;
+ }
+ if (self->encoder.mutex) {
+ tsk_mutex_destroy(&self->encoder.mutex);
+ }
+ TSK_FREE(self->encoder.rtp.ptr);
+ self->encoder.rtp.size = 0;
+ self->encoder.rotation = 0; // reset rotation
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(end)");
+ return 0;
+}
+
+int tdav_codec_vp8_close_decoder(tdav_codec_vp8_t* self)
+{
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(begin)");
+ if (self->decoder.initialized) {
+ vpx_codec_destroy(&self->decoder.context);
+ self->decoder.initialized = tsk_false;
+ }
+ TSK_FREE(self->decoder.accumulator);
+ self->decoder.accumulator_size = 0;
+ self->decoder.accumulator_pos = 0;
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(end)");
+
+ return 0;
+}
+
+/* ============ VP8 RTP packetizer/depacketizer ================= */
+
+
+static void tdav_codec_vp8_encap(tdav_codec_vp8_t* self, const vpx_codec_cx_pkt_t *pkt)
+{
+ tsk_bool_t non_ref, is_keyframe, part_start;
+ uint8_t *frame_ptr;
+ uint32_t part_size, part_ID, pkt_size, index;
+
+ if (!self || !pkt || !pkt->data.frame.buf || !pkt->data.frame.sz) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return;
+ }
+
+ index = 0;
+ frame_ptr = pkt->data.frame.buf;
+ pkt_size = (uint32_t)pkt->data.frame.sz;
+ non_ref = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE);
+ is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
+
+
+#if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
+ part_ID = pkt->data.frame.partition_id;
+ part_start = tsk_true;
+ part_size = pkt_size;
+ while (index < part_size) {
+ uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
+ tdav_codec_vp8_rtp_callback(
+ self,
+ &frame_ptr[index],
+ frag_size,
+ part_ID,
+ part_start,
+ non_ref,
+ ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0 && (index + frag_size) == part_size) // RTP marker?
+ );
+ part_start = tsk_false;
+ index += frag_size;
+ }
+#else
+ // first partition (contains modes and motion vectors)
+ part_ID = 0; // The first VP8 partition(containing modes and motion vectors) MUST be labeled with PartID = 0
+ part_start = tsk_true;
+ part_size = (frame_ptr[2] << 16) | (frame_ptr[1] << 8) | frame_ptr[0];
+ part_size = (part_size >> 5) & 0x7FFFF;
+ if (part_size > pkt_size) {
+ TSK_DEBUG_ERROR("part_size is > pkt_size(%u,%u)", part_size, pkt_size);
+ return;
+ }
+
+ // first,first,....partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
+ while (index<part_size) {
+ uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
+ tdav_codec_vp8_rtp_callback(self, &frame_ptr[index], frag_size, part_ID, part_start, non_ref, tsk_false);
+ part_start = tsk_false;
+ index += frag_size;
+ }
+
+ // second,third,... partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
+ // FIXME: low FEC
+ part_start = tsk_true;
+ while (index<pkt_size) {
+ if (part_start) {
+ /* PartID SHOULD be incremented for each subsequent partition,
+ but MAY be kept at 0 for all packets. PartID MUST NOT be larger
+ than 8.
+ */
+ part_ID++;
+ }
+ part_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (pkt_size - index));
+
+ tdav_codec_vp8_rtp_callback(self, &frame_ptr[index], part_size, part_ID, part_start, non_ref, (index + part_size)==pkt_size);
+ index += part_size;
+ /*
+ If more than one packet in an encoded frame contains the
+ same PartID, the S bit MUST NOT be set for any other packet than
+ the first packet with that PartID.
+ */
+ part_start = tsk_false;
+ }
+#endif /* VPX_CODEC_USE_OUTPUT_PARTITION */
+}
+
+static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data, tsk_size_t size, uint32_t partID, tsk_bool_t part_start, tsk_bool_t non_ref, tsk_bool_t last)
+{
+ tsk_size_t paydesc_and_hdr_size = TDAV_VP8_PAY_DESC_SIZE;
+ tsk_bool_t has_hdr;
+ /* draft-ietf-payload-vp8-04 - 4.2. VP8 Payload Descriptor
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |X|R|N|S|PartID | (REQUIRED)
+ +-+-+-+-+-+-+-+-+
+ X: |I|L|T|K| RSV | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+ I: |M| PictureID | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+ L: | TL0PICIDX | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+ T/K: |TID|Y| KEYIDX | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+
+ draft-ietf-payload-vp8-04 - 4.3. VP8 Payload Header
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |Size0|H| VER |P|
+ +-+-+-+-+-+-+-+-+
+ | Size1 |
+ +-+-+-+-+-+-+-+-+
+ | Size2 |
+ +-+-+-+-+-+-+-+-+
+ | Bytes 4..N of |
+ | VP8 payload |
+ : :
+ +-+-+-+-+-+-+-+-+
+ | OPTIONAL RTP |
+ | padding |
+ : :
+ +-+-+-+-+-+-+-+-+
+ */
+
+ /*
+ Note that the header is present only in packets which have the S bit equal to one and the
+ PartID equal to zero in the payload descriptor.
+ */
+ if ((has_hdr = (part_start && partID == 0))) {
+ has_hdr = tsk_true;
+ paydesc_and_hdr_size += 0; // encoded data already contains payload header?
+ }
+
+ if (!data || !size) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return;
+ }
+ if (self->encoder.rtp.size < (size + paydesc_and_hdr_size)) {
+ if (!(self->encoder.rtp.ptr = tsk_realloc(self->encoder.rtp.ptr, (size + paydesc_and_hdr_size)))) {
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return;
+ }
+ self->encoder.rtp.size = (size + paydesc_and_hdr_size);
+ }
+ memcpy((self->encoder.rtp.ptr + paydesc_and_hdr_size), data, size);
+
+ /* VP8 Payload Descriptor */
+ // |X|R|N|S|PartID|
+ self->encoder.rtp.ptr[0] = (partID & 0x0F) // PartID
+ | ((part_start << 4) & 0x10)// S
+ | ((non_ref << 5) & 0x20) // N
+ // R = 0
+#if TDAV_VP8_DISABLE_EXTENSION
+ | (0x00) // X=0
+#else
+ | (0x80) // X=1
+#endif
+ ;
+
+#if !TDAV_VP8_DISABLE_EXTENSION
+ // X: |I|L|T|K| RSV |
+ self->encoder.rtp.ptr[1] = 0x80; // I = 1, L = 0, T = 0, K = 0, RSV = 0
+ // I: |M| PictureID |
+ self->encoder.rtp.ptr[2] = (0x80 | ((self->encoder.pic_id >> 8) & 0x7F)); // M = 1 (PictureID on 15 bits)
+ self->encoder.rtp.ptr[3] = (self->encoder.pic_id & 0xFF);
+#endif
+
+ /* 4.2. VP8 Payload Header */
+ //if(has_hdr) {
+ // already part of the encoded stream
+ //}
+
+ // Send data over the network
+ if (TMEDIA_CODEC_VIDEO(self)->out.callback) {
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->encoder.rtp.ptr;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (size + TDAV_VP8_PAY_DESC_SIZE);
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1. / (double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = last;
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
+}
+
+#endif /* HAVE_LIBVPX */
OpenPOWER on IntegriCloud