lighttpd对flv视频文件的拖动一种变形

当前流行的视频网站,关于视频的在线拖动观看技术的小试,

1.flv文件

flv视频文件,想大家都比较清楚,只要从关键帧的位置切开,并加上"FLV\x1\x1\0\0\0\x9\0\0\0\x9"公共头标志信息,后对任何支持flv视频文件dencoder的播放器都是可以正常播放的

"FLV\x1\x1\0\0\0\x9\0\0\0\x9"+keyframe(body)

2.lighttpd中的mod_flv_streaming.c模块(重点)

http://www.lighttpd.net/

http://blog.lighttpd.net/articles/2006/03/09/flv-streaming-with-lighttpd

这里通过http的get方法,向服务器请求指定的视频片段

形如:http://youserver.com/flv/abcdefg.flv?start=12345

mod_flv_streaming.c默认的方式是,从start(文件的物理位置offset)的开始,默认到文件结束。

相关服务器代码如下:

for (k = 0; k < p->conf.extensions->used; k++) {
		data_string *ds = (data_string *)p->conf.extensions->data[k];
		int ct_len = ds->value->used - 1;

		if (ct_len > s_len) continue;
		if (ds->value->used == 0) continue;

		if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
			data_string *get_param;
			stat_cache_entry *sce = NULL;
			buffer *b;
			int start;
			char *err = NULL;
			/* if there is a start=[0-9]+ in the header use it as start,
			 * otherwise send the full file */

			array_reset(p->get_params);
			buffer_copy_string_buffer(p->query_str, con->uri.query);
			split_get_params(p->get_params, p->query_str);
/*这个是重点,获得客户端的播放器通过get方法传来的start(文件拖动的开始位置offset)*/
			if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
				return HANDLER_GO_ON;
			}

			/* too short */
			if (get_param->value->used < 2) return HANDLER_GO_ON;

			/* check if it is a number */
			start = strtol(get_param->value->ptr, &err, 10);
			if (*err != '\0') {
				return HANDLER_GO_ON;
			}

			if (start <= 0) return HANDLER_GO_ON;

			/* check if start is > filesize */
			if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
				return HANDLER_GO_ON;
			}

			if (start > sce->st.st_size) {
				return HANDLER_GO_ON;
			}

			/* we are safe now, let's build a flv header */
			b = chunkqueue_get_append_buffer(con->write_queue);
                        
                        /*准备发送给客户端播放器的flv公共头信息13字节*/
			buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));
                        
                        /*发送seek到的拖动关键桢的位置keyframe, 并计算出从拖动点到该视频文件结束的长度*/
			http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start);
                       /* http相关信息的填充*/
			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));

			con->file_finished = 1;
                       /*done, 处理拖动完成*/
			return HANDLER_FINISHED

3对客户端的播放器而言,服务器对flv文件的拖动支持是透明的,

只要播放器安照合法的get方法向服务器请求,即可得到可以播放的视频片段,

但是这里的start=xxxx,一定是flv视频文件的关键桢位置,否则不能播放

一般而言,对flv视频文件可以使用

yamdi

http://yamdi.sourceforge.net/

加上metadata信息,即可完美支持拖动,

播放器可以再第一次请求视频文件的时候获得该视频的metadata信息(即包含关键桢,时间,等等与视频相关的信息)

4.以上讨论的,都是lighttpd-mod_flv_streaming.c默认的对flv的拖动支持,

其实可以,做一些简单的扩展,

可以增加end参数,即是增加拖动的结束参数,

假设,很多用户并不会观看视频到结束,

每次请求N字节

http://youserver.com/flv/abcdefg.flv?start=23456&end=23456+N

播放器可以增加这样的功能,

a.先请求某一段视频

b.当当前视频片段没有播放完毕之前,不会去请求下一个片段,

只有当当前视频片段在即将播放结束的时候,再去请求下一个片段,

这样,当用户,看了前面的某一片段后,突然关闭该播放页,以至于不会白白浪费掉那已经download到本地,但是并没有观看的视频,节余带宽

粗略代码实现如下:

if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "start"))) 
{

    /* too short */
    if (get_param->value->used < 2) 
					return HANDLER_GO_ON;

			    /* check if it is a number */
			    start = strtol(get_param->value->ptr, &err, 10);
			    if (*err != '\0') 
				{
				    return HANDLER_GO_ON;
			    }
			    
			    /* check if tflvbegin is >= 0 */
			    if (start < 0) 
					return HANDLER_GO_ON;

			    /* check if start is > filesize */
			    if (start > sce->st.st_size) 
				{
				    return HANDLER_GO_ON;
			    }
			}
			else 
			{
				return HANDLER_GO_ON;
			}
                        
			/* if there is a start=[0-9]+ in the header use it as end,
			 * otherwise send the full file */
/*这里重点,仿照lighttpd如何获得get方法的中的start,同理获得end参数, 并做一些必要的合法性检查*/
			if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "end"))) 
			{
			    /* too short */
			    if (get_param->value->used < 2) 
					return HANDLER_GO_ON;

			    /* check if it is a number */
			    end = strtol(get_param->value->ptr, &err, 10);
			    if (*err != '\0') 
				{
				    return HANDLER_GO_ON;
			    }

			    /*参数检查,必须*/
			    /* check if end is > 0 
			     * check if start < end 
			     * make sure star > 0 
			     * */
			    if (end <= 0 || start >= end) 
					return HANDLER_GO_ON;

			    /* check if end is > filesize */
			    if (end > sce->st.st_size) 
				{
					//return HANDLER_GO_ON;
					tflvend = sce->st.st_size; /* path tflvend is not right */
			    }
			}
			else 
			{
				return HANDLER_GO_ON;
			
    
   }

/* we are safe now, let's build a flv header */
			b = chunkqueue_get_append_buffer(con->write_queue);
                        
                        /*准备发送给客户端播放器的flv公共头信息13字节*/
			buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));

                      /*这个是重点,以前默认的是发送的数据长度是用 fileseize-start, 现在要用end替代filesize, 即是 end-start*/
			http_chunk_append_file(srv, con, con->physical.path, start, end - start);
			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));
			con->file_finished = 1;
			return HANDLER_FINISHED

-------------------------------------

gamil:[email protected]