Filter是linphone源码的精髓,Filter可以比作过滤器,负责对数据进行不同功能的操作,如解码、编码、显示、滤噪等等. 往往一个流程需要很多个Filter来组成,只有经过一层层的过滤,才能得到【纯净】的数据. 比如视频接收显示流程,需要经过rtprecv、decoder、tee、jpegwriter、display几个Filter. 下面我们来详细地讲述Filter.

定义

struct _MSFilterDesc{
	MSFilterId id;
	const char *name;
	const char *text;
	MSFilterCategory category;
	const char *enc_fmt;
	int ninputs;
	int noutputs;
	MSFilterFunc init;
	MSFilterFunc preprocess;
	MSFilterFunc process;
	MSFilterFunc postprocess;
	MSFilterFunc uninit;
	MSFilterMethod *methods;
	unsigned int flags;
};

struct _MSFilter{
	MSFilterDesc *desc; //描述,指定过滤id、名称、函数指针(初始化、预处理、处理、反初始化)
	ms_mutex_t lock;
	MSQueue **inputs; //输入源
	MSQueue **outputs; //输出源
	struct _MSFactory *factory;
	void *padding;
	void *data;
	struct _MSTicker *ticker;
	MSList *notify_callbacks;
	uint32_t last_tick;
	MSFilterStats *stats;
	int postponed_task;
	bool_t seen;
};

PS: 每种类型的Filter仅有一个Desc,该Desc定于实现文件中,并通过EXPORT来引用.

创建

创建一个Filter的方式比较多,可以根据id、name、desc等等,如下是五种创建方式,足够用.

MS2_PUBLIC MSFilter * ms_filter_create_encoder(const char *mime); //创建编码Filter
MS2_PUBLIC MSFilter * ms_filter_create_decoder(const char *mime); //创建解码Filter
MS2_PUBLIC MSFilter *ms_filter_new(MSFilterId id); //根据Id创建Filter
MS2_PUBLIC MSFilter *ms_filter_new_from_name(const char *name); //根据名称创建Filter
MS2_PUBLIC MSFilter *ms_filter_new_from_desc(MSFilterDesc *desc); //根据描述创建Filter

连接

在文章开头我已声明过,一个流程可能会有多个Filter,那么问题来了,我们如何关联这些Filter?这里大家可以想到化学实验课时,我们一般会使用一个导管把上一步的输出接到下一步的输入,这里也一样,我们可以通过ms_filter_link来连接2个Filter.

// 连接f1的outputs[pin1]与f2的inputs[pin2]
int ms_filter_link(MSFilter *f1, int pin1, MSFilter *f2, int pin2){
	MSQueue *q; //可以理解为导管
	...
	q=ms_queue_new(f1,pin1,f2,pin2);
	f1->outputs[pin1]=q;
	f2->inputs[pin2]=q;
	return 0;
}

断开

有连接操作就有断开操作,当需要断开2个Filter又如何操作呢,那就是去掉导管

int ms_filter_unlink(MSFilter *f1, int pin1, MSFilter *f2, int pin2){
	MSQueue *q;
	...
	q=f1->outputs[pin1];
	f1->outputs[pin1]=f2->inputs[pin2]=0;
	ms_queue_destroy(q);
	return 0;
}

销毁

为了保证资源的正确释放,需要调用销毁接口

void ms_filter_destroy(MSFilter *f)

实例 - 视频接收显示

启动视频

videostream.c

int video_stream_start_with_source (VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam* cam, MSFilter* source, bool_t discard_decode_error){
		...
		/* 创建 */
		stream->ms.decoder=ms_filter_create_decoder(pt->mime_type);
		if (stream->ms.decoder==NULL){
			/* big problem: we don't have a registered decoderfor this payload...*/
			ms_error("videostream.c: No decoder available for payload %i:%s.",payload,pt->mime_type);
			return -1;
		}
		...
		/* display logic */
		stream->output = ms_filter_new_from_name (stream->display_name);
		stream->ms.rtprecv = ms_filter_new (MS_RTP_RECV_ID);
		ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);

		if (stream->output_performs_decoding == FALSE) {
			stream->jpegwriter=ms_filter_new(MS_JPEG_WRITER_ID);
			if (stream->jpegwriter){
				stream->tee2=ms_filter_new(MS_TEE_ID);
			}
		}
		...
		/* 连接 */
		ms_connection_helper_start (&ch);
		ms_connection_helper_link (&ch,stream->ms.rtprecv,-1,0);
		if (stream->output_performs_decoding == FALSE) {
			if (stream->itcsink){
				ms_connection_helper_link(&ch,stream->tee3,0,0);
				ms_filter_link(stream->tee3,1,stream->itcsink,0);
				configure_itc(stream);
			}
			ms_connection_helper_link(&ch,stream->ms.decoder,0,0);
		}
		if (stream->tee2){
			ms_connection_helper_link (&ch,stream->tee2,0,0);
			ms_filter_link(stream->tee2,1,stream->jpegwriter,0);
		}
		if (stream->output!=NULL)
			ms_connection_helper_link (&ch,stream->output,0,-1);
		...
}

PS: tee是一分多过滤器,一个输入多个输出 可以看出视频输出经过的过滤器依次是:ms.rtprecv -》 ms.decoder -》 tee2 -》jpegwriter-》output

ms_connection_helper_link可以理解队列,filter依次加到其尾部

停止视频

videostream.c

void
video_stream_stop (VideoStream * stream)
{
...
			} else if (stream->ms.rtprecv){
				MSConnectionHelper h;
				ms_connection_helper_start (&h);
				ms_connection_helper_unlink (&h,stream->ms.rtprecv,-1,0);
				if (stream->output_performs_decoding == FALSE) {
					if (stream->itcsink){
						ms_connection_helper_unlink(&h,stream->tee3,0,0);
						ms_filter_unlink(stream->tee3,1,stream->itcsink,0);
					}
					ms_connection_helper_unlink (&h,stream->ms.decoder,0,0);
				}
				if (stream->tee2){
					ms_connection_helper_unlink (&h,stream->tee2,0,0);
					ms_filter_unlink(stream->tee2,1,stream->jpegwriter,0);
				}
				if(stream->output)
					ms_connection_helper_unlink (&h,stream->output,0,-1);
				if (stream->tee && stream->output && stream->output2==NULL)
					ms_filter_unlink(stream->tee,1,stream->output,1);
			}
		}
	}
...
}