📄 videoenc.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.*/#ifdef HAVE_CONFIG_H#include "mediastreamer-config.h"#endif#ifdef HAVE_LIBAVCODEC_AVCODEC_H#include <libavcodec/avcodec.h>#else#include <ffmpeg/avcodec.h>#endif#include "mediastreamer2/msfilter.h"#include "mediastreamer2/msvideo.h"#include "mediastreamer2/msticker.h"#ifdef _WIN32#include <ws2tcpip.h>#else#include <netinet/in.h> /* ntohl(3) */#endif#include "rfc2429.h"static bool_t avcodec_initialized=FALSE;void ms_ffmpeg_check_init(){ if(!avcodec_initialized){ avcodec_init(); avcodec_register_all(); avcodec_initialized=TRUE; }}typedef struct EncState{ AVCodecContext av_context; AVCodec *av_codec; enum CodecID codec; mblk_t *comp_buf; MSVideoSize vsize; int mtu; /* network maximum transmission unit in bytes */ int profile; float fps; int maxbr; int qmin; bool_t req_vfu;}EncState;static int enc_set_fps(MSFilter *f, void *arg){ EncState *s=(EncState*)f->data; s->fps=*(float*)arg; return 0;}static int enc_get_fps(MSFilter *f, void *arg){ EncState *s=(EncState*)f->data; *(float*)arg=s->fps; return 0;}static int enc_set_vsize(MSFilter *f,void *arg){ EncState *s=(EncState*)f->data; s->vsize=*(MSVideoSize*)arg; return 0;}static int enc_get_vsize(MSFilter *f,void *arg){ EncState *s=(EncState*)f->data; *(MSVideoSize*)arg=s->vsize; return 0;}static int enc_set_mtu(MSFilter *f,void *arg){ EncState *s=(EncState*)f->data; s->mtu=*(int*)arg; return 0;}static bool_t parse_video_fmtp(const char *fmtp, float *fps, MSVideoSize *vsize){ char *tmp=ms_strdup(fmtp); char *semicolon; char *equal; bool_t ret=TRUE; ms_message("parsing %s",fmtp); /*extract fisrt pair */ if ((semicolon=strchr(tmp,';'))!=NULL){ *semicolon='\0'; } if ((equal=strchr(tmp,'='))!=NULL){ int divider; *equal='\0'; if (strcasecmp(tmp,"CIF")==0){ if (vsize->width>=MS_VIDEO_SIZE_CIF_W){ vsize->width=MS_VIDEO_SIZE_CIF_W; vsize->height=MS_VIDEO_SIZE_CIF_H; } }else if (strcasecmp(tmp,"QCIF")==0){ vsize->width=MS_VIDEO_SIZE_QCIF_W; vsize->height=MS_VIDEO_SIZE_QCIF_H; }else{ ms_warning("unsupported video size %s",tmp); ret=FALSE; } divider=atoi(equal+1); if (divider!=0){ float newfps=29.97/divider; if (*fps>newfps) *fps=newfps; }else{ ms_warning("Could not find video fps"); ret=FALSE; } }else ret=FALSE; ms_free(tmp); return ret;}static int enc_add_fmtp(MSFilter *f,void *arg){ EncState *s=(EncState*)f->data; const char *fmtp=(const char*)arg; char val[10]; if (fmtp_get_value(fmtp,"profile",val,sizeof(val))){ s->profile=atoi(val); }else parse_video_fmtp(fmtp,&s->fps,&s->vsize); return 0;}static int enc_req_vfu(MSFilter *f, void *unused){ EncState *s=(EncState*)f->data; s->req_vfu=TRUE; return 0;}static void enc_init(MSFilter *f, enum CodecID codec){ EncState *s=(EncState *)ms_new(EncState,1); f->data=s; ms_ffmpeg_check_init(); s->profile=0;/*always default to profile 0*/ s->comp_buf=allocb(32000,0); s->fps=15; s->mtu=ms_get_payload_max_size()-2;/*-2 for the H263 payload header*/ s->maxbr=500000; s->codec=codec; s->vsize.width=MS_VIDEO_SIZE_CIF_W; s->vsize.height=MS_VIDEO_SIZE_CIF_H; s->qmin=2; s->req_vfu=FALSE; s->av_context.codec=NULL;}static void enc_h263_init(MSFilter *f){ enc_init(f,CODEC_ID_H263P);}static void enc_mpeg4_init(MSFilter *f){ enc_init(f,CODEC_ID_MPEG4);}static void enc_snow_init(MSFilter *f){ enc_init(f,CODEC_ID_SNOW);}static void prepare(EncState *s){ AVCodecContext *c=&s->av_context; avcodec_get_context_defaults(c); /* put codec parameters */ c->bit_rate=(float)s->maxbr*0.7; c->bit_rate_tolerance=(float)c->bit_rate/(s->fps-1); if (s->codec!=CODEC_ID_SNOW && s->maxbr<256000){ /*snow does not like 1st pass rate control*/ /*and rate control eats too much cpu with CIF high fps pictures*/ c->rc_max_rate=(float)s->maxbr*0.8; c->rc_min_rate=c->bit_rate; c->rc_buffer_size=c->rc_max_rate; }else{ /*use qmin instead*/ c->qmin=s->qmin; } ms_message("Codec bitrate set to %i",c->bit_rate); c->width = s->vsize.width; c->height = s->vsize.height; c->time_base.num = 1; c->time_base.den = (int)s->fps; c->gop_size=(int)s->fps*5; /*emit I frame every 5 seconds*/ c->pix_fmt=PIX_FMT_YUV420P; if (s->codec==CODEC_ID_SNOW){ c->strict_std_compliance=-2; } }static void prepare_h263(EncState *s){ AVCodecContext *c=&s->av_context; /* we don't use the rtp_callback but use rtp_mode that forces ffmpeg to insert Start Codes as much as possible in the bitstream */#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) c->rtp_mode = 1;#endif c->rtp_payload_size = s->mtu/2; if (s->profile==0){ s->codec=CODEC_ID_H263; }else{ c->flags|=CODEC_FLAG_H263P_UMV; c->flags|=CODEC_FLAG_AC_PRED; c->flags|=CODEC_FLAG_H263P_SLICE_STRUCT; /* c->flags|=CODEC_FLAG_OBMC; c->flags|=CODEC_FLAG_AC_PRED; */ s->codec=CODEC_ID_H263P; }}static void prepare_mpeg4(EncState *s){ AVCodecContext *c=&s->av_context; c->max_b_frames=0; /*don't use b frames*/ c->flags|=CODEC_FLAG_AC_PRED; c->flags|=CODEC_FLAG_H263P_UMV; /*c->flags|=CODEC_FLAG_QPEL;*/ /*don't enable this one: this forces profile_level to advanced simple profile */ c->flags|=CODEC_FLAG_4MV; c->flags|=CODEC_FLAG_GMC; c->flags|=CODEC_FLAG_LOOP_FILTER; c->flags|=CODEC_FLAG_H263P_SLICE_STRUCT;}static void enc_uninit(MSFilter *f){ EncState *s=(EncState*)f->data; if (s->comp_buf!=NULL) freemsg(s->comp_buf); ms_free(s);}#if 0static void enc_set_rc(EncState *s, AVCodecContext *c){ int factor=c->width/MS_VIDEO_SIZE_QCIF_W; c->rc_min_rate=0; c->bit_rate=400; /* this value makes around 100kbit/s at QCIF=2 */ c->rc_max_rate=c->bit_rate+1; c->rc_buffer_size=20000*factor; /* food recipe */}#endifstatic void enc_preprocess(MSFilter *f){ EncState *s=(EncState*)f->data; int error; prepare(s); if (s->codec==CODEC_ID_H263P || s->codec==CODEC_ID_H263) prepare_h263(s); else if (s->codec==CODEC_ID_MPEG4) prepare_mpeg4(s); else if (s->codec==CODEC_ID_SNOW){ /**/ }else { ms_error("Unsupported codec id %i",s->codec); return; } s->av_codec=avcodec_find_encoder(s->codec); if (s->av_codec==NULL){ ms_error("could not find encoder for codec id %i",s->codec); return; } error=avcodec_open(&s->av_context, s->av_codec); if (error!=0) { ms_error("avcodec_open() failed: %i",error); return; } ms_debug("image format is %i.",s->av_context.pix_fmt); ms_message("qmin=%i qmax=%i",s->av_context.qmin,s->av_context.qmax);}static void enc_postprocess(MSFilter *f){ EncState *s=(EncState*)f->data; if (s->av_context.codec!=NULL){ avcodec_close(&s->av_context); s->av_context.codec=NULL; }}static void add_rfc2190_header(mblk_t **packet, AVCodecContext *context){ mblk_t *header; header = allocb(4, 0); memset(header->b_wptr, 0, 4); // assume video size is CIF or QCIF if (context->width == 352 && context->height == 288) header->b_wptr[1] = 0x60; else header->b_wptr[1] = 0x40; if (context->coded_frame->pict_type != FF_I_TYPE) header->b_wptr[1] |= 0x10; header->b_wptr += 4; header->b_cont = *packet; *packet = header;}static int get_gbsc(uint8_t *psc, uint8_t *end){ int len = end-psc; uint32_t buf; int i, j, k; k = len; for (i = 2; i < len-4; i++) { buf = *((uint32_t *)(psc+i)); for (j = 0; j < 8; j++) { if (((buf >> j) & 0x00FCFFFF) == 0x00800000) {/*PSC*/ i += 2; k=i; break; } else if (((buf >> j) & 0x0080FFFF) == 0x00800000) {/*GBSC*/ i += 2; k = i; break; } } } return k;}static void rfc2190_generate_packets(MSFilter *f, EncState *s, mblk_t *frame, uint32_t timestamp){ mblk_t *packet=NULL; while (frame->b_rptr<frame->b_wptr){ packet=dupb(frame); frame->b_rptr=packet->b_wptr=packet->b_rptr+get_gbsc(packet->b_rptr, MIN(packet->b_rptr+s->mtu,frame->b_wptr)); add_rfc2190_header(&packet, &s->av_context); mblk_set_timestamp_info(packet,timestamp); ms_queue_put(f->outputs[0],packet); } /* the marker bit is set on the last packet, if any.*/ mblk_set_marker_info(packet,TRUE);}static void mpeg4_fragment_and_send(MSFilter *f,EncState *s,mblk_t *frame, uint32_t timestamp){ uint8_t *rptr; mblk_t *packet=NULL; int len; for (rptr=frame->b_rptr;rptr<frame->b_wptr;){ len=MIN(s->mtu,(frame->b_wptr-rptr)); packet=dupb(frame); packet->b_rptr=rptr; packet->b_wptr=rptr+len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -