michael@0: /******************************************************************** michael@0: * * michael@0: * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * michael@0: * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * michael@0: * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * michael@0: * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * michael@0: * * michael@0: * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * michael@0: * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * michael@0: * * michael@0: ******************************************************************** michael@0: michael@0: function: michael@0: last mod: $Id: decapiwrapper.c 13596 2007-08-23 20:05:38Z tterribe $ michael@0: michael@0: ********************************************************************/ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "apiwrapper.h" michael@0: #include "decint.h" michael@0: #include "theora/theoradec.h" michael@0: michael@0: static void th_dec_api_clear(th_api_wrapper *_api){ michael@0: if(_api->setup)th_setup_free(_api->setup); michael@0: if(_api->decode)th_decode_free(_api->decode); michael@0: memset(_api,0,sizeof(*_api)); michael@0: } michael@0: michael@0: static void theora_decode_clear(theora_state *_td){ michael@0: if(_td->i!=NULL)theora_info_clear(_td->i); michael@0: memset(_td,0,sizeof(*_td)); michael@0: } michael@0: michael@0: static int theora_decode_control(theora_state *_td,int _req, michael@0: void *_buf,size_t _buf_sz){ michael@0: return th_decode_ctl(((th_api_wrapper *)_td->i->codec_setup)->decode, michael@0: _req,_buf,_buf_sz); michael@0: } michael@0: michael@0: static ogg_int64_t theora_decode_granule_frame(theora_state *_td, michael@0: ogg_int64_t _gp){ michael@0: return th_granule_frame(((th_api_wrapper *)_td->i->codec_setup)->decode,_gp); michael@0: } michael@0: michael@0: static double theora_decode_granule_time(theora_state *_td,ogg_int64_t _gp){ michael@0: return th_granule_time(((th_api_wrapper *)_td->i->codec_setup)->decode,_gp); michael@0: } michael@0: michael@0: static const oc_state_dispatch_vtable OC_DEC_DISPATCH_VTBL={ michael@0: (oc_state_clear_func)theora_decode_clear, michael@0: (oc_state_control_func)theora_decode_control, michael@0: (oc_state_granule_frame_func)theora_decode_granule_frame, michael@0: (oc_state_granule_time_func)theora_decode_granule_time, michael@0: }; michael@0: michael@0: static void th_info2theora_info(theora_info *_ci,const th_info *_info){ michael@0: _ci->version_major=_info->version_major; michael@0: _ci->version_minor=_info->version_minor; michael@0: _ci->version_subminor=_info->version_subminor; michael@0: _ci->width=_info->frame_width; michael@0: _ci->height=_info->frame_height; michael@0: _ci->frame_width=_info->pic_width; michael@0: _ci->frame_height=_info->pic_height; michael@0: _ci->offset_x=_info->pic_x; michael@0: _ci->offset_y=_info->pic_y; michael@0: _ci->fps_numerator=_info->fps_numerator; michael@0: _ci->fps_denominator=_info->fps_denominator; michael@0: _ci->aspect_numerator=_info->aspect_numerator; michael@0: _ci->aspect_denominator=_info->aspect_denominator; michael@0: switch(_info->colorspace){ michael@0: case TH_CS_ITU_REC_470M:_ci->colorspace=OC_CS_ITU_REC_470M;break; michael@0: case TH_CS_ITU_REC_470BG:_ci->colorspace=OC_CS_ITU_REC_470BG;break; michael@0: default:_ci->colorspace=OC_CS_UNSPECIFIED;break; michael@0: } michael@0: switch(_info->pixel_fmt){ michael@0: case TH_PF_420:_ci->pixelformat=OC_PF_420;break; michael@0: case TH_PF_422:_ci->pixelformat=OC_PF_422;break; michael@0: case TH_PF_444:_ci->pixelformat=OC_PF_444;break; michael@0: default:_ci->pixelformat=OC_PF_RSVD; michael@0: } michael@0: _ci->target_bitrate=_info->target_bitrate; michael@0: _ci->quality=_info->quality; michael@0: _ci->keyframe_frequency_force=1<<_info->keyframe_granule_shift; michael@0: } michael@0: michael@0: int theora_decode_init(theora_state *_td,theora_info *_ci){ michael@0: th_api_info *apiinfo; michael@0: th_api_wrapper *api; michael@0: th_info info; michael@0: api=(th_api_wrapper *)_ci->codec_setup; michael@0: /*Allocate our own combined API wrapper/theora_info struct. michael@0: We put them both in one malloc'd block so that when the API wrapper is michael@0: freed, the info struct goes with it. michael@0: This avoids having to figure out whether or not we need to free the info michael@0: struct in either theora_info_clear() or theora_clear().*/ michael@0: apiinfo=(th_api_info *)_ogg_calloc(1,sizeof(*apiinfo)); michael@0: if(apiinfo==NULL)return OC_FAULT; michael@0: /*Make our own copy of the info struct, since its lifetime should be michael@0: independent of the one we were passed in.*/ michael@0: *&apiinfo->info=*_ci; michael@0: /*Convert the info struct now instead of saving the the one we decoded with michael@0: theora_decode_header(), since the user might have modified values (i.e., michael@0: color space, aspect ratio, etc. can be specified from a higher level). michael@0: The user also might be doing something "clever" with the header packets if michael@0: they are not using an Ogg encapsulation.*/ michael@0: oc_theora_info2th_info(&info,_ci); michael@0: /*Don't bother to copy the setup info; th_decode_alloc() makes its own copy michael@0: of the stuff it needs.*/ michael@0: apiinfo->api.decode=th_decode_alloc(&info,api->setup); michael@0: if(apiinfo->api.decode==NULL){ michael@0: _ogg_free(apiinfo); michael@0: return OC_EINVAL; michael@0: } michael@0: apiinfo->api.clear=(oc_setup_clear_func)th_dec_api_clear; michael@0: _td->internal_encode=NULL; michael@0: /*Provide entry points for ABI compatibility with old decoder shared libs.*/ michael@0: _td->internal_decode=(void *)&OC_DEC_DISPATCH_VTBL; michael@0: _td->granulepos=0; michael@0: _td->i=&apiinfo->info; michael@0: _td->i->codec_setup=&apiinfo->api; michael@0: return 0; michael@0: } michael@0: michael@0: int theora_decode_header(theora_info *_ci,theora_comment *_cc,ogg_packet *_op){ michael@0: th_api_wrapper *api; michael@0: th_info info; michael@0: int ret; michael@0: api=(th_api_wrapper *)_ci->codec_setup; michael@0: /*Allocate an API wrapper struct on demand, since it will not also include a michael@0: theora_info struct like the ones that are used in a theora_state struct.*/ michael@0: if(api==NULL){ michael@0: _ci->codec_setup=_ogg_calloc(1,sizeof(*api)); michael@0: if(_ci->codec_setup==NULL)return OC_FAULT; michael@0: api=(th_api_wrapper *)_ci->codec_setup; michael@0: api->clear=(oc_setup_clear_func)th_dec_api_clear; michael@0: } michael@0: /*Convert from the theora_info struct instead of saving our own th_info michael@0: struct between calls. michael@0: The user might be doing something "clever" with the header packets if they michael@0: are not using an Ogg encapsulation, and we don't want to break this.*/ michael@0: oc_theora_info2th_info(&info,_ci); michael@0: /*We rely on the fact that theora_comment and th_comment structures are michael@0: actually identical. michael@0: Take care not to change this fact unless you change the code here as michael@0: well!*/ michael@0: ret=th_decode_headerin(&info,(th_comment *)_cc,&api->setup,_op); michael@0: /*We also rely on the fact that the error return code values are the same, michael@0: and that the implementations of these two functions return the same set of michael@0: them. michael@0: Note that theora_decode_header() really can return OC_NOTFORMAT, even michael@0: though it is not currently documented to do so.*/ michael@0: if(ret<0)return ret; michael@0: th_info2theora_info(_ci,&info); michael@0: return 0; michael@0: } michael@0: michael@0: int theora_decode_packetin(theora_state *_td,ogg_packet *_op){ michael@0: th_api_wrapper *api; michael@0: ogg_int64_t gp; michael@0: int ret; michael@0: if(!_td||!_td->i||!_td->i->codec_setup)return OC_FAULT; michael@0: api=(th_api_wrapper *)_td->i->codec_setup; michael@0: ret=th_decode_packetin(api->decode,_op,&gp); michael@0: if(ret<0)return OC_BADPACKET; michael@0: _td->granulepos=gp; michael@0: return 0; michael@0: } michael@0: michael@0: int theora_decode_YUVout(theora_state *_td,yuv_buffer *_yuv){ michael@0: th_api_wrapper *api; michael@0: th_dec_ctx *decode; michael@0: th_ycbcr_buffer buf; michael@0: int ret; michael@0: if(!_td||!_td->i||!_td->i->codec_setup)return OC_FAULT; michael@0: api=(th_api_wrapper *)_td->i->codec_setup; michael@0: decode=(th_dec_ctx *)api->decode; michael@0: if(!decode)return OC_FAULT; michael@0: ret=th_decode_ycbcr_out(decode,buf); michael@0: if(ret>=0){ michael@0: _yuv->y_width=buf[0].width; michael@0: _yuv->y_height=buf[0].height; michael@0: _yuv->y_stride=buf[0].stride; michael@0: _yuv->uv_width=buf[1].width; michael@0: _yuv->uv_height=buf[1].height; michael@0: _yuv->uv_stride=buf[1].stride; michael@0: _yuv->y=buf[0].data; michael@0: _yuv->u=buf[1].data; michael@0: _yuv->v=buf[2].data; michael@0: } michael@0: return ret; michael@0: }