📄 theora.c
字号:
/*mediastreamer2 library - modular sound and video processing and streamingCopyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include "mediastreamer2/msfilter.h"#include "mediastreamer2/msticker.h"#include "mediastreamer2/msvideo.h"#include <theora/theora.h>typedef struct EncState{ theora_state tstate; theora_info tinfo; yuv_buffer yuv; mblk_t *packed_conf; uint64_t start_time; uint64_t conf_time; unsigned int mtu;} EncState;static void enc_init(MSFilter *f){ EncState *s=(EncState *)ms_new(EncState,1); theora_info_init(&s->tinfo); s->tinfo.width=MS_VIDEO_SIZE_CIF_W; s->tinfo.height=MS_VIDEO_SIZE_CIF_H; s->tinfo.frame_width=MS_VIDEO_SIZE_CIF_W; s->tinfo.frame_height=MS_VIDEO_SIZE_CIF_H; s->tinfo.offset_x=0; s->tinfo.offset_y=0; s->tinfo.target_bitrate=500000; s->tinfo.pixelformat=OC_PF_420; s->tinfo.fps_numerator=15; s->tinfo.fps_denominator=1; s->tinfo.aspect_numerator=1; s->tinfo.aspect_denominator=1; s->tinfo.colorspace=OC_CS_UNSPECIFIED; s->tinfo.dropframes_p=0; s->tinfo.quick_p=1; s->tinfo.quality=63; s->tinfo.keyframe_auto_p=1; s->tinfo.keyframe_frequency=64; s->tinfo.keyframe_frequency_force=64; s->tinfo.keyframe_data_target_bitrate=s->tinfo.target_bitrate*1.2; s->tinfo.keyframe_auto_threshold=80; s->tinfo.keyframe_mindistance=8; s->tinfo.noise_sensitivity=1; s->packed_conf=NULL; s->start_time=0; s->conf_time=0; s->mtu=ms_get_payload_max_size()-6; f->data=s;}static void enc_uninit(MSFilter *f){ EncState *s=(EncState*)f->data; theora_info_clear(&s->tinfo); ms_free(s);}static int enc_set_vsize(MSFilter *f, void*data){ MSVideoSize *vs=(MSVideoSize*)data; EncState *s=(EncState*)f->data; s->tinfo.width=vs->width; s->tinfo.height=vs->height; s->tinfo.frame_width=vs->width; s->tinfo.frame_height=vs->height; return 0;}static int enc_get_vsize(MSFilter *f, void *data){ EncState *s=(EncState*)f->data; MSVideoSize *vs=(MSVideoSize*)data; vs->width=s->tinfo.width; vs->height=s->tinfo.height; return 0;}static int enc_add_attr(MSFilter *f, void*data){ /*const char *attr=(const char*)data; EncState *s=(EncState*)f->data;*/ return 0;}static int enc_set_fps(MSFilter *f, void *data){ float *fps=(float*)data; EncState *s=(EncState*)f->data; s->tinfo.fps_numerator=*fps; s->tinfo.keyframe_frequency=(*fps)*5; s->tinfo.keyframe_frequency_force=(*fps)*5; return 0;}static int enc_get_fps(MSFilter *f, void *data){ EncState *s=(EncState*)f->data; float *fps=(float*)data; *fps=s->tinfo.fps_numerator; return 0;}static int enc_set_br(MSFilter *f, void*data){ int br=*(int*)data; EncState *s=(EncState*)f->data; MSVideoSize vsize; float fps; float codecbr=(float)br; vsize.width=s->tinfo.width; vsize.height=s->tinfo.height; fps=s->tinfo.fps_numerator; s->tinfo.target_bitrate=codecbr*0.8; s->tinfo.keyframe_data_target_bitrate=codecbr; /*those default settings would need to be affined*/ if (br>=1024000){ vsize.width = MS_VIDEO_SIZE_4CIF_W; vsize.height = MS_VIDEO_SIZE_4CIF_H; s->tinfo.quality=32; fps=15; }else if (br>=512000){ vsize.width = MS_VIDEO_SIZE_CIF_W; vsize.height = MS_VIDEO_SIZE_CIF_H; s->tinfo.quality=32; fps=15; }else if (br>=256000){ vsize.width = MS_VIDEO_SIZE_CIF_W; vsize.height = MS_VIDEO_SIZE_CIF_H; s->tinfo.quality=5; fps=12; }else if(br>=128000){ vsize.width=MS_VIDEO_SIZE_QCIF_W; vsize.height=MS_VIDEO_SIZE_QCIF_H; s->tinfo.quality=20; fps=10; }else if(br>=64000){ vsize.width=MS_VIDEO_SIZE_QCIF_W; vsize.height=MS_VIDEO_SIZE_QCIF_H; s->tinfo.quality=7; fps=7; } enc_set_vsize(f,&vsize); enc_set_fps(f,&fps); return 0;}static int enc_set_mtu(MSFilter *f, void*data){ EncState *s=(EncState*)f->data; s->mtu=*(int*)data; return 0;}#define THEORA_RAW_DATA 0#define THEORA_PACKED_CONF 1#define THEORA_COMMENT 2#define THEORA_RESERVED 3#define NOT_FRAGMENTED 0#define START_FRAGMENT 1#define CONT_FRAGMENT 2#define END_FRAGMENT 3static inline void payload_header_set(uint8_t *buf, uint32_t ident, uint8_t ft, uint8_t tdt, uint8_t pkts){ uint32_t tmp; tmp=((ident&0xFFFFFF)<<8) | ((ft&0x3)<<6) | ((tdt&0x3)<<4) | (pkts&0xf); *((uint32_t*)buf)=htonl(tmp);}static inline uint32_t payload_header_get_ident(uint8_t *buf){ uint32_t *tmp=(uint32_t*)buf; return (ntohl(*tmp)>>8) & 0xFFFFFF;}static inline uint32_t payload_header_get_tdt(uint8_t *buf){ uint32_t *tmp=(uint32_t*)buf; return ((ntohl(*tmp))>>4) & 0x3;}static inline uint32_t payload_header_get_ft(uint8_t *buf){ uint32_t *tmp=(uint32_t*)buf; return ((ntohl(*tmp))>>6) & 0x3;}static inline uint32_t payload_header_get_pkts(uint8_t *buf){ uint32_t *tmp=(uint32_t*)buf; return ntohl(*tmp) & 0xf;}static int create_packed_conf(EncState *s){ ogg_packet p; theora_state *tstate=&s->tstate; mblk_t *h,*t; if (theora_encode_header(tstate,&p)!=0){ ms_error("theora_encode_header() error."); return -1; } h=allocb(p.bytes,0); memcpy(h->b_wptr,p.packet,p.bytes); h->b_wptr+=p.bytes; if (theora_encode_tables(tstate,&p)!=0){ ms_error("theora_encode_tables error."); freemsg(h); return -1; } t=allocb(p.bytes,0); memcpy(t->b_wptr,p.packet,p.bytes); t->b_wptr+=p.bytes; h->b_cont=t; msgpullup(h,-1); s->packed_conf=h; return 0;}static void enc_preprocess(MSFilter *f){ EncState *s=(EncState*)f->data; int err; if ((err=theora_encode_init(&s->tstate,&s->tinfo))!=0){ ms_error("error in theora_encode_init() : %i !",err); } s->yuv.y_width=s->tinfo.width; s->yuv.y_height=s->tinfo.height; s->yuv.y_stride=s->tinfo.width; s->yuv.uv_width=s->tinfo.width/2; s->yuv.uv_height=s->tinfo.height/2; s->yuv.uv_stride=s->tinfo.width/2; create_packed_conf(s); s->conf_time=0; s->start_time=f->ticker->time;}static void enc_postprocess(MSFilter *f){ EncState *s=(EncState*)f->data; theora_clear(&s->tstate); //If preprocess is called after postprocess, //then we loose all info... //theora_info_clear(&s->tinfo); if (s->packed_conf) { freemsg(s->packed_conf); s->packed_conf=NULL; }}static void enc_fill_yuv(yuv_buffer *yuv, mblk_t *im){ yuv->y=(uint8_t*)im->b_rptr; yuv->u=(uint8_t*)im->b_rptr+(yuv->y_stride*yuv->y_height); yuv->v=(uint8_t*)yuv->u+(yuv->uv_stride*yuv->uv_height);}static void packetize_and_send(MSFilter *f, EncState *s, mblk_t *om, uint32_t timestamp, uint8_t tdt){ mblk_t *packet; mblk_t *h; int npackets=0; static const int ident=0xdede; while(om!=NULL){ if (om->b_wptr-om->b_rptr>=s->mtu){ packet=dupb(om); packet->b_wptr=packet->b_rptr+s->mtu; om->b_rptr=packet->b_wptr; }else { packet=om; om=NULL; } ++npackets; h=allocb(6,0); if (npackets==1){ if (om==NULL) payload_header_set(h->b_wptr,ident,NOT_FRAGMENTED,tdt,1); else payload_header_set(h->b_wptr,ident,START_FRAGMENT,tdt,1); }else{ if (om==NULL) payload_header_set(h->b_wptr,ident,END_FRAGMENT,tdt,1); else payload_header_set(h->b_wptr,ident,CONT_FRAGMENT,tdt,1); } h->b_wptr+=4; *((uint16_t*)h->b_wptr)=htons(msgdsize(packet)); h->b_wptr+=2; h->b_cont=packet; mblk_set_timestamp_info(h,timestamp); ms_debug("sending theora frame of size %i",msgdsize(h)); ms_queue_put(f->outputs[0],h); }}bool_t need_send_conf(EncState *s, uint64_t elapsed){#ifdef AMD_HACK if (elapsed<5000 && elapsed>=s->conf_time){ s->conf_time+=500; return TRUE; }#else /*send immediately then 10 seconds later */ if ( (elapsed<1000 && s->conf_time==0) || (elapsed>10000 && s->conf_time==1)){ s->conf_time++; return TRUE; }#endif return FALSE;}static void enc_process(MSFilter *f){ mblk_t *im,*om; ogg_packet op; EncState *s=(EncState*)f->data; uint64_t timems=f->ticker->time; uint32_t timestamp=timems*90; uint64_t elapsed=timems-s->start_time; while((im=ms_queue_get(f->inputs[0]))!=NULL){ /*for the firsts frames only send theora packed conf*/ om=NULL; if (need_send_conf(s,elapsed)){ if (s->packed_conf) { om=dupmsg(s->packed_conf); ms_message("sending theora packed conf (%i bytes)",msgdsize(om)); packetize_and_send(f,s,om,timestamp,THEORA_PACKED_CONF); }else { ms_error("No packed conf to send."); } }else{ enc_fill_yuv(&s->yuv,im); ms_debug("subtmitting yuv frame to theora encoder..."); if (theora_encode_YUVin(&s->tstate,&s->yuv)!=0){ ms_error("theora_encode_YUVin error."); }else{ if (theora_encode_packetout(&s->tstate,0,&op)==1){ ms_debug("Got theora coded frame"); om=allocb(op.bytes,0); memcpy(om->b_wptr,op.packet,op.bytes); om->b_wptr+=op.bytes; packetize_and_send(f,s,om,timestamp,THEORA_RAW_DATA); } } } freemsg(im); }}static MSFilterMethod enc_methods[]={ { MS_FILTER_SET_VIDEO_SIZE, enc_set_vsize }, { MS_FILTER_SET_FPS, enc_set_fps }, { MS_FILTER_GET_VIDEO_SIZE, enc_get_vsize }, { MS_FILTER_GET_FPS, enc_get_fps }, { MS_FILTER_ADD_ATTR, enc_add_attr }, { MS_FILTER_SET_BITRATE, enc_set_br }, { MS_FILTER_SET_MTU, enc_set_mtu }, { 0 , NULL }};#ifdef _MSC_VERMSFilterDesc ms_theora_enc_desc={ MS_THEORA_ENC_ID, "MSTheoraEnc", "The theora video encoder from xiph.org", MS_FILTER_ENCODER, "theora", 1, 1, enc_init, enc_preprocess, enc_process, enc_postprocess, enc_uninit, enc_methods};#elseMSFilterDesc ms_theora_enc_desc={ .id=MS_THEORA_ENC_ID, .name="MSTheoraEnc", .text="The open-source and royalty-free 'theora' video codec from xiph.org", .category=MS_FILTER_ENCODER, .enc_fmt="theora", .ninputs=1, .noutputs=1, .init=enc_init, .preprocess=enc_preprocess, .process=enc_process, .postprocess=enc_postprocess, .uninit=enc_uninit, .methods=enc_methods};#endifMS_FILTER_DESC_EXPORT(ms_theora_enc_desc)typedef struct DecState{ theora_state tstate; theora_info tinfo; mblk_t *yuv; mblk_t *curframe; bool_t ready;}DecState;static void dec_init(MSFilter *f){ DecState *s=(DecState *)ms_new(DecState,1); s->ready=FALSE; theora_info_init(&s->tinfo); s->yuv=NULL; s->curframe=NULL; f->data=s;}static void dec_uninit(MSFilter *f){ DecState *s=(DecState*)f->data; if (s->yuv!=NULL) freemsg(s->yuv); if (s->curframe!=NULL) freemsg(s->curframe); theora_info_clear(&s->tinfo); ms_free(s);}static bool_t dec_init_theora(DecState *s, ogg_packet *op){ theora_comment tcom; static const int ident_packet_size=42; theora_comment_init(&tcom); tcom.vendor="dummy"; op->b_o_s=1; if (theora_decode_header(&s->tinfo,&tcom,op)==0){ op->packet+=ident_packet_size; op->bytes-=ident_packet_size; /*recall once to decode tables*/ if (theora_decode_header(&s->tinfo,&tcom,op)==0){ if (theora_decode_init(&s->tstate,&s->tinfo)==0){ ms_debug("theora decoder ready, pixfmt=%i", s->tinfo.pixelformat); return TRUE; } }else{ ms_warning("error decoding theora tables"); } }else{ ms_warning("error decoding theora header"); } return FALSE;}/* remove payload header and agregates fragmented packets */static mblk_t *dec_unpacketize(MSFilter *f, DecState *s, mblk_t *im, int *tdt){ uint8_t ft; *tdt=payload_header_get_tdt((uint8_t*)im->b_rptr); ft=payload_header_get_ft((uint8_t*)im->b_rptr); im->b_rptr+=6; if (ft==NOT_FRAGMENTED) return im; if (ft==START_FRAGMENT){ if (s->curframe!=NULL) freemsg(s->curframe); s->curframe=im; }else if (ft==CONT_FRAGMENT){ if (s->curframe!=NULL) concatb(s->curframe,im); else freemsg(im); }else{/*end fragment*/ if (s->curframe!=NULL){ mblk_t *ret; concatb(s->curframe,im); msgpullup(s->curframe,-1); ret=s->curframe; s->curframe=NULL; return ret; }else freemsg(im); } return NULL;}static void dec_process_frame(MSFilter *f, DecState *s, ogg_packet *op){ yuv_buffer yuv; if (theora_decode_packetin(&s->tstate,op)==0){ if (theora_decode_YUVout(&s->tstate,&yuv)==0){ mblk_t *om; int i; int ylen=yuv.y_width*yuv.y_height; int uvlen=yuv.uv_width*yuv.uv_height; ms_debug("Got yuv buffer from theora decoder"); if (s->yuv==NULL){ int len=(ylen)+(2*uvlen); s->yuv=allocb(len,0); } om=dupb(s->yuv); for(i=0;i<yuv.y_height;++i){ memcpy(om->b_wptr,yuv.y+yuv.y_stride*i,yuv.y_width); om->b_wptr+=yuv.y_width; } for(i=0;i<yuv.uv_height;++i){ memcpy(om->b_wptr,yuv.u+yuv.uv_stride*i,yuv.uv_width); om->b_wptr+=yuv.uv_width; } for(i=0;i<yuv.uv_height;++i){ memcpy(om->b_wptr,yuv.v+yuv.uv_stride*i,yuv.uv_width); om->b_wptr+=yuv.uv_width; } ms_queue_put(f->outputs[0],om); } }else{ ms_warning("theora decoding error"); }}static void dec_process(MSFilter *f){ mblk_t *im; mblk_t *m; ogg_packet op; int tdt; DecState *s=(DecState*)f->data; while( (im=ms_queue_get(f->inputs[0]))!=0) { m=dec_unpacketize(f,s,im,&tdt); if (m!=NULL){ /* now in im we have only the theora data*/ op.packet=(uint8_t*)m->b_rptr; op.bytes=m->b_wptr-m->b_rptr; op.b_o_s=0; op.e_o_s=0; op.granulepos=0; op.packetno=0; if (tdt!=THEORA_RAW_DATA) /*packed conf*/ { if (!s->ready){ if (dec_init_theora(s,&op)) s->ready=TRUE; } }else{ if (s->ready){ dec_process_frame(f,s,&op); }else{ ms_warning("skipping theora packet because decoder was not initialized yet with theora header and tables"); } } freemsg(m); } }}#ifdef _MSC_VERMSFilterDesc ms_theora_dec_desc={ MS_THEORA_DEC_ID, "MSTheoraDec", "The theora video decoder from xiph.org", MS_FILTER_DECODER, "theora", 1, 1, dec_init, NULL, dec_process, NULL, dec_uninit, NULL};#elseMSFilterDesc ms_theora_dec_desc={ .id=MS_THEORA_DEC_ID, .name="MSTheoraDec", .text="The theora video decoder from xiph.org", .category=MS_FILTER_DECODER, .enc_fmt="theora", .ninputs=1, .noutputs=1, .init=dec_init, .process=dec_process, .uninit=dec_uninit};#endifMS_FILTER_DESC_EXPORT(ms_theora_dec_desc)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -