From 1ebf5a5fcda0c9154e22ed02404fd46525a7fd9f Mon Sep 17 00:00:00 2001 From: bossiel Date: Wed, 10 Aug 2011 22:59:15 +0000 Subject: Move deprecated v1.0 from trunk to branches --- tinyDAV/src/codecs/h261/tdav_codec_h261.c | 539 ++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 tinyDAV/src/codecs/h261/tdav_codec_h261.c (limited to 'tinyDAV/src/codecs/h261/tdav_codec_h261.c') 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..780f2be --- /dev/null +++ b/tinyDAV/src/codecs/h261/tdav_codec_h261.c @@ -0,0 +1,539 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: 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_h261.c + * @brief H.261 codec plugin. + * RTP payloader follows RFC 4587 + * + * @author Mamadou Diop + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#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_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)->fps; + h261->encoder.context->width = TMEDIA_CODEC_VIDEO(h261)->width; + h261->encoder.context->height = TMEDIA_CODEC_VIDEO(h261)->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)->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)->width; + h261->decoder.context->height = TMEDIA_CODEC_VIDEO(h261)->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; + } + /* Flip */ +#if FLIP_ENCODED_PICT + tdav_converter_video_flip(h261->encoder.picture, h261->encoder.context->height); +#endif + + // 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 decoder.accumulator_pos = 0; + return 0; + } + *out_max_size = xsize; + } + + /* decode the picture */ + av_init_packet(&packet); + packet.size = 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; +#if FLIP_DECODED_PICT + tdav_converter_video_flip(h261->decoder.picture, h261->decoder.context->height); +#endif + /* copy picture into a linear buffer */ + avpicture_layout((AVPicture *)h261->decoder.picture, h261->decoder.context->pix_fmt, h261->decoder.context->width, h261->decoder.context->height, + *out_data, retsize); + } + /* in all cases: reset accumulator */ + h261->decoder.accumulator_pos = 0; + } + + return retsize; +} + +tsk_bool_t tdav_codec_h261_fmtp_match(const tmedia_codec_t* codec, const char* fmtp) +{ + int ret; + unsigned maxbr, fps, width, height; + tmedia_codec_video_t* h261 = (tmedia_codec_video_t*)codec; + + if(!(ret = tmedia_codec_parse_fmtp(fmtp, &maxbr, &fps, &width, &height))){ + h261->max_br = maxbr * 1000; + h261->fps = fps; + h261->width = width; + h261->height = height; + return tsk_true; + } + else{ + TSK_DEBUG_WARN("Failed to match fmtp [%s]", fmtp); + return tsk_false; + } +} + +char* tdav_codec_h261_fmtp_get(const tmedia_codec_t* self) +{ +#if 0 + return tsk_strdup("CIF=2/MaxBR=3840;QCIF=2/MaxBR=1920"); +#else + return tsk_strdup("QCIF=2"); +#endif +} + +int tdav_codec_h261_fmtp_set(tmedia_codec_t* self, const char* fmtp) +{ + TSK_DEBUG_INFO("remote fmtp=%s", fmtp); + return 0; +} + +/* 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, + "H261", + "H261 codec", + TMEDIA_CODEC_FORMAT_H261, + tsk_false, + 90000, // rate + + /* audio */ + { 0 }, + + /* video */ + {176, 144, 15}, + + tdav_codec_h261_open, + tdav_codec_h261_close, + tdav_codec_h261_encode, + tdav_codec_h261_decode, + tdav_codec_h261_fmtp_match, + tdav_codec_h261_fmtp_get, + tdav_codec_h261_fmtp_set +}; +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) +{ + +} + + + +#endif /* HAVE_FFMPEG */ \ No newline at end of file -- cgit v1.1