在项目中用到了mediastream2,mediastream2是一个框架,引擎,它驱动系统的整个流程,从接收rtp媒体流并解析到媒体解码到显示到android手机屏幕上,都是由mediastream2来驱动完成的。
mediastream2是由一个个filter来完成,我们可以把filter当做一个单独的数据处理模块,它就像一个盒子,有输入和输出接口,数据从输入接口进来,处理完成后,再从输出接口出来进入到下一个filter中,多个filter连接在一起,就组成了一个系统循环。在我们的系统中,主要流程是ipcam发送rtp媒体流,系统接收rtp媒体流并解析,然后进行解码,再显示到android手机屏幕上,因此我们定义了三个filter,分别是:RTP Recv filter(RTP媒体流接收),Video Decoder filter(视频解码)和Android Display filter(视频输出显示),其基本流程如下:
RTP Recv----------------->Video Decoder------------------------------> Android Display
首先我们要调用ms_init()这个函数,这个函数初始化mediastream2。
void ms_init(){
int i;
MSSndCardManager *cm;
#if defined(ENABLE_NLS)
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#endif
#if !defined(_WIN32_WCE)
if (getenv("MEDIASTREAMER_DEBUG")!=NULL){
ortp_set_log_level_mask(ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
}
#endif
#ifdef ANDROID
ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
ortp_set_log_handler(ms_android_log_handler);
#endif
ms_message("Mediastreamer2 " MEDIASTREAMER_VERSION " (git: " GIT_VERSION ") starting.");
/* register builtin MSFilter's */
for (i=0;ms_filter_descs[i]!=NULL;i++){
ms_filter_register(ms_filter_descs[i]);
}
ms_message("Registering all soundcard handlers");
cm=ms_snd_card_manager_get();
for (i=0;ms_snd_card_descs[i]!=NULL;i++){
ms_snd_card_manager_register_desc(cm,ms_snd_card_descs[i]);
}
#ifdef VIDEO_ENABLED
ms_message("Registering all webcam handlers");
{
MSWebCamManager *wm;
wm=ms_web_cam_manager_get();
for (i=0;ms_web_cam_descs[i]!=NULL;i++){
ms_web_cam_manager_register_desc(wm,ms_web_cam_descs[i]);
}
}
#if !defined(NO_FFMPEG)
ms_ffmpeg_check_init();
__register_ffmpeg_encoders_if_possible();
#endif
#endif
#ifdef PACKAGE_PLUGINS_DIR
ms_message("Loading plugins");
ms_load_plugins(PACKAGE_PLUGINS_DIR);
#endif
#if defined(ANDROID) && defined (VIDEO_ENABLED)
if (1) {
libmsandroidopengldisplay_init();
} else {
if (!libmsandroiddisplay_init()) {
libmsandroiddisplaybad_init();
}
}
#endif
ms_message("ms_init() done");
}
在这个函数里面,ms_filter_descs定义了目前支持的所有的filter,进一步分析
MSFilterDesc * ms_filter_descs[]={
&ms_alaw_dec_desc,
&ms_alaw_enc_desc,
&ms_ulaw_dec_desc,
&ms_ulaw_enc_desc,
&ms_rtp_send_desc,
&ms_rtp_recv_desc,
&ms_dtmf_gen_desc,
&ms_ice_desc,
&ms_tee_desc,
&ms_conf_desc,
&ms_join_desc,
&ms_volume_desc,
&ms_void_sink_desc,
//&ms_speex_dec_desc,
//&ms_speex_enc_desc,
//&ms_speex_ec_desc,
&ms_file_player_desc,
&ms_file_rec_desc,
//&ms_resample_desc,
&ms_equalizer_desc,
//&ms_gsm_enc_desc,
//&ms_gsm_dec_desc,
&ms_tone_detector_desc,
&ms_audio_mixer_desc,
&ms_g722_dec_desc,
&ms_g722_enc_desc,
#ifdef VIDEO_ENABLED
&ms_mpeg4_enc_desc,
&ms_mpeg4_dec_desc,
&ms_h263_enc_desc,
&ms_h263_dec_desc,
&ms_h264_dec_desc,
&ms_pix_conv_desc,
&ms_size_conv_desc,
//&ms_vp8_enc_desc,
//&ms_vp8_dec_desc,
#endif
NULL
};
我们可以看到系统定义了很多filter,比如ms_rtp_recv_desc是RTP接收filter,ms_h264_dec_desc是H.264解码filter等等。
MSFilterDesc ms_h264_dec_desc={
.id=MS_H264_DEC_ID,
.name="MSH264Dec",
.text="A H264 decoder based on ffmpeg project.",
.category=MS_FILTER_DECODER,
.enc_fmt="H264",
.ninputs=1,
.noutputs=1,
.init=dec_init,
.process=dec_process,
.uninit=dec_uninit,
.methods=h264_dec_methods
};
在filter定义里面,id是用来标示每个filter的唯一标示符;name是起得一个名称;text是描述,上面就表示该filter是基于ffmpeg的H.264解码;category是分类,有解码器,编码器和其它三种类型;enc_fmt表示媒体类型;ninputs表示input个数,可能每个filter有多个输入接口;noutputs表示输出接口;init定义初始化函数,在调用filter之前必须 调用;process是最核心的处理函数,filter从input端接收数据就调用该函数进行数据处理;uninit定义退出函数;methods表示额外定义的一些函数,使用这个接口可以对filter进行一些参数设置工作,比如在上面的h.264解码filter中定义了专门的函数用来设置h.264解码所需要的fmtp信息。除了mediastream2自带的这些filter以外,我们还可以自己定义filter,按照上面的格式来定义,然后通过ms_filter_register函数注册进来就可以使用了。在我们系统中我们使用ms_rtp_recv_desc来完成rtp的接收解析,使用ms_h264_dec_desc来完成视频解码,使用ms_android_opengl_display_desc来完成在android上的显示。
定义完成了3个filter,接收流,解码并显示,这三个filter是如何创建并连接在一起并工作起来呢?见下面的代码注释
MSConnectionHelper ch;
/*根据ipcam video的codec来创建相应的解码器filter,创建失败,则退出,在我们系统里面一般是h.264,但我们可以根据rtp媒体类型在自适应选择对应的解码filter*/
stream->decoder=ms_filter_create_decoder(ipcam_get_codec("video")); // 创建解码filter
if (stream->decoder==NULL)
{
/* big problem: we don't have a registered decoderfor this payload...*/
ms_error(": No decoder available ");
return -1;
}
/*创建rtp接受filter*/
stream->rtprecv = ms_filter_new (MS_RTP_RECV_ID);//创建RTP接收filter,通过MS_RTP_RECV_ID找到ms_rtp_recv_desc这个filter
/*创建rtp接收filter,需要设置rtp接收filter的会话,会话主要是接收媒体流的socket,告诉filter再哪个socket上接收rtp数据,并限制接收什么样的rtp数据,通过payload type*/
/*stream->session这个是_RtpSession*, 是mediastream2定义的结构体*/
ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,stream->session);
ms_message("ssap_ipcam_client_open:session rtpsocket=%d,rtcpsocket=%d",stream->session->,stream->session->);
/*创建output filter,默认是MSAndroidDisplay,表示输出到android上,通过MSAndroidDisplay找到ms_android_opengl_display_desc*/
displayName = ms_strdup("MSAndroidDisplay");
stream->output=ms_filter_new_from_name(displayName);
/*以下是设置fmtp,非常重要,如果不添加fmtp,将会解码失败,mpeg4需要的fmtp在rtsp中是在sdp中
带过来的,因此要获取fmtp*/
char fmtp[1000];
memset(fmtp,0,sizeof(fmtp));
sprintf(fmtp,"config=%s",ipcam_get_fmtp("video"));
ms_message("ipcam fmtp=%s",fmtp);
ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)fmtp);
format=MS_YUV420P;
ms_filter_call_method(stream->decoder,MS_FILTER_SET_PIX_FMT,&format);
int tmp;
/*configure the display window */
disp_size.width=MS_VIDEO_SIZE_CIF_W;
disp_size.height=MS_VIDEO_SIZE_CIF_H;
tmp=1;
/*设置一些参数,主要用来显示*/
ms_filter_call_method(stream->output,MS_FILTER_SET_VIDEO_SIZE,&disp_size);
ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_ENABLE_AUTOFIT,&tmp);
ms_filter_call_method(stream->output,MS_FILTER_SET_PIX_FMT,&format);
//ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&stream->corner);
if (stream->window_id != 0)
{
ms_message("v2 sam display windows id=%d,stream=%p",stream->window_id,stream);
/*设置显示的窗口id,在哪个窗口上显示video*/
ms_filter_call_method(stream->output, MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID,stream->window_id);
}
/*以上完成了3个filter的创建,接下来将3个filter串联起来*/
/* and connect the filters */
ms_connection_helper_start (&ch);
ms_connection_helper_link (&ch,stream->rtprecv,-1,0);
ms_connection_helper_link (&ch,stream->decoder,0,0);
ms_connection_helper_link (&ch,stream->output,0,-1);
/*filter串联起来了以后,开始启动定时器,整个系统流程开始运转起来*/
/* create the ticker */
stream->ticker = ms_ticker_new();
ms_ticker_set_name(stream->ticker,"Video MSTicker");
/* attach the graphs */
if (stream->source)
ms_ticker_attach (stream->ticker, stream->source);
if (stream->rtprecv)
ms_ticker_attach (stream->ticker, stream->rtprecv);
至此,mediastream2就开始运转起来了