summaryrefslogtreecommitdiffstats
path: root/tinyDAV/src/codecs/theora/tdav_codec_theora.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV/src/codecs/theora/tdav_codec_theora.c')
-rw-r--r--tinyDAV/src/codecs/theora/tdav_codec_theora.c707
1 files changed, 707 insertions, 0 deletions
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..67fb3e6
--- /dev/null
+++ b/tinyDAV/src/codecs/theora/tdav_codec_theora.c
@@ -0,0 +1,707 @@
+/*
+* Copyright (C) 2009-2010 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>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#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 "tsk_time.h"
+#include "tsk_params.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define THEORA_RTP_PAYLOAD_SIZE 900
+#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, .... */
+
+/* 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_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 ================= */
+
+#define tdav_codec_theora_fmtp_set tsk_null
+
+int tdav_codec_theora_open(tmedia_codec_t* self)
+{
+ int ret;
+ int size;
+ float bitRate = 64000.f;
+
+ 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(!(theora->encoder.codec = avcodec_find_encoder(CODEC_ID_THEORA))){
+ TSK_DEBUG_ERROR("Failed to find Theora encoder");
+ return -2;
+ }
+ theora->encoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(theora->encoder.context);
+
+ theora->encoder.context->pix_fmt = PIX_FMT_YUV420P;
+ theora->encoder.context->time_base.num = 1;
+ theora->encoder.context->time_base.den = TMEDIA_CODEC_VIDEO(theora)->fps;
+ theora->encoder.context->width = TMEDIA_CODEC_VIDEO(theora)->width;
+ theora->encoder.context->height = TMEDIA_CODEC_VIDEO(theora)->height;
+
+ //theora->encoder.context->mb_qmin = theora->encoder.context->qmin = 4;
+ //theora->encoder.context->mb_qmax = theora->encoder.context->qmax = 4;
+ theora->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
+
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ bitRate = 64000.f;
+ break;
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ bitRate = 128000.f;
+ break;
+ }
+
+ theora->encoder.context->thread_count = 1;
+ theora->encoder.context->rtp_payload_size = THEORA_RTP_PAYLOAD_SIZE;
+ theora->encoder.context->opaque = tsk_null;
+ theora->encoder.context->bit_rate = (int) (bitRate * 0.80f);
+ theora->encoder.context->bit_rate_tolerance = (int) (bitRate * 0.20f);
+ theora->encoder.context->gop_size = TMEDIA_CODEC_VIDEO(theora)->fps*2; // each 2 seconds
+
+ // Picture (YUV 420)
+ if(!(theora->encoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create encoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(theora->encoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, theora->encoder.context->width, theora->encoder.context->height);
+ if(!(theora->encoder.buffer = tsk_calloc(size, sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate encoder buffer");
+ return -2;
+ }
+
+ // Open encoder
+ if((ret = avcodec_open(theora->encoder.context, theora->encoder.codec)) < 0){
+ TSK_DEBUG_ERROR("Failed to open Theora encoder");
+ return ret;
+ }
+
+ //
+ // Decoder
+ //
+ if(!(theora->decoder.codec = avcodec_find_decoder(CODEC_ID_THEORA))){
+ TSK_DEBUG_ERROR("Failed to find Theora decoder");
+ return -2;
+ }
+ theora->decoder.context = avcodec_alloc_context();
+ avcodec_get_context_defaults(theora->decoder.context);
+
+ theora->decoder.context->pix_fmt = PIX_FMT_YUV420P;
+ theora->decoder.context->width = TMEDIA_CODEC_VIDEO(theora)->width;
+ theora->decoder.context->height = TMEDIA_CODEC_VIDEO(theora)->height;
+
+ // Picture (YUV 420)
+ if(!(theora->decoder.picture = avcodec_alloc_frame())){
+ TSK_DEBUG_ERROR("Failed to create decoder picture");
+ return -2;
+ }
+ avcodec_get_frame_defaults(theora->decoder.picture);
+
+ size = avpicture_get_size(PIX_FMT_YUV420P, theora->decoder.context->width, theora->decoder.context->height);
+ if(!(theora->decoder.accumulator = tsk_calloc((size + FF_INPUT_BUFFER_PADDING_SIZE), sizeof(uint8_t)))){
+ TSK_DEBUG_ERROR("Failed to allocate decoder buffer");
+ return -2;
+ }
+
+ if(!(theora->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(theora->decoder.context, theora->decoder.codec)) < 0){
+ // TSK_DEBUG_ERROR("Failed to open Theora decoder");
+ // 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
+ //
+ if(theora->encoder.context){
+ avcodec_close(theora->encoder.context);
+ av_free(theora->encoder.context);
+ theora->encoder.context = tsk_null;
+ }
+ if(theora->encoder.picture){
+ av_free(theora->encoder.picture);
+ }
+ if(theora->encoder.buffer){
+ TSK_FREE(theora->encoder.buffer);
+ }
+
+ //
+ // Decoder
+ //
+ if(theora->decoder.context){
+ avcodec_close(theora->decoder.context);
+ if(theora->decoder.context->extradata){
+ TSK_FREE(theora->decoder.context->extradata);
+ theora->decoder.context->extradata_size = 0;
+ }
+ av_free(theora->decoder.context);
+ theora->decoder.context = tsk_null;
+ }
+ if(theora->decoder.picture){
+ av_free(theora->decoder.picture);
+ theora->decoder.picture = tsk_null;
+ }
+ if(theora->decoder.accumulator){
+ TSK_FREE(theora->decoder.accumulator);
+ }
+
+ 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;
+ }
+ /* Flip */
+#if FLIP_ENCODED_PICT
+ tdav_converter_video_flip(theora->encoder.picture, theora->encoder.context->height);
+#endif
+
+ // Encode data
+ //theora->encoder.picture->pts = tsk_time_epoch();
+ theora->encoder.picture->pts = AV_NOPTS_VALUE;
+ //theora->encoder.picture->pict_type = FF_I_TYPE;
+ 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);
+ }
+
+ 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 = 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 || !got_picture_ptr){
+ TSK_DEBUG_WARN("Failed to decode the buffer");
+ }
+ else{
+ retsize = xsize;
+#if FLIP_DECODED_PICT
+ tdav_converter_video_flip(theora->decoder.picture, theora->decoder.context->height);
+#endif
+ /* 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, theora->decoder.context->width, theora->decoder.context->height,
+ *out_data, 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 = 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_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ tsk_bool_t ret = tsk_true; // accept decoding any size
+ tsk_params_L_t* params = tsk_null;
+ tmedia_codec_video_t* theora = (tmedia_codec_video_t*)codec;
+
+ if((params = tsk_params_fromstring(fmtp, ";", tsk_true))){
+ int width = tsk_params_get_param_value_as_int(params, "width");
+ int height = tsk_params_get_param_value_as_int(params, "height");
+
+ // Set Encoding size
+ switch(codec->bl){
+ case tmedia_bl_low:
+ default:
+ if(width<=176 && height<=144){
+ theora->width = width, theora->height = height;
+ }
+ else{
+ theora->width = 176, theora->height = 144;
+ }
+ break;
+
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ if(width<=352 && height<=288){
+ theora->width = width, theora->height = height;
+ }
+ else{
+ theora->width = 352, theora->height = 288;
+ }
+ break;
+ }
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+
+ return ret;
+}
+
+char* tdav_codec_theora_fmtp_get(const tmedia_codec_t* self)
+{
+ switch(self->bl){
+ case tmedia_bl_low:
+ default:
+ return tsk_strdup("sampling=YCbCr-4:2:0; width=176; height=144");
+ break;
+ case tmedia_bl_medium:
+ case tmedia_bl_hight:
+ return tsk_strdup("sampling=YCbCr-4:2:0; width=352; height=288");
+ break;
+ }
+}
+
+
+
+/* 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 */
+ //TSK_OBJECT_SAFE_FREE(theora->conf_data);
+ }
+ 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,
+ "theora",
+ "Theora Codec",
+ TMEDIA_CODEC_FORMAT_THEORA,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144, 15},
+
+ tdav_codec_theora_open,
+ tdav_codec_theora_close,
+ tdav_codec_theora_encode,
+ tdav_codec_theora_decode,
+ tdav_codec_theora_fmtp_match,
+ tdav_codec_theora_fmtp_get,
+ tdav_codec_theora_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tdav_codec_theora_plugin_def_t = &tdav_codec_theora_plugin_def_s;
+
+
+
+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_epoch()){
+ 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_epoch();
+ 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] = pay_size>>8, pay_hdr[5] = 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)->callback){
+ TMEDIA_CODEC_VIDEO(self)->callback(TMEDIA_CODEC_VIDEO(self)->callback_data, self->rtp.ptr, (pay_size + sizeof(pay_hdr)), (3003* (30/TMEDIA_CODEC_VIDEO(self)->fps)), (size == 0));
+ }
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_FFMPEG */ \ No newline at end of file
OpenPOWER on IntegriCloud