Mplayer学习笔记1--打开流媒体

mplayer打开流媒体

main函数开始:

2729 while (player_idle_mode &&!filename) {
2730 play_tree_t * entry = NULL;
2731 mp_cmd_t * cmd;
2732 while (!(cmd =mp_input_get_cmd(0,1,0))) { // wait for command
2733 if (mpctx->video_out &&vo_config_count) mpctx->video_out->check_events();
2734 usec_sleep(20000);
2735 }
2736 switch (cmd->id) {
2737 case MP_CMD_LOADFILE:
2738 // prepare a treeentry with the new filename
2739 entry =play_tree_new();
2740 play_tree_add_file(entry, cmd->args[0].v.s);
2741 // The entry is addedto the main playtree after the switch().
2742 break;
2743 case MP_CMD_LOADLIST:
2744 entry =parse_playlist_file(cmd->args[0].v.s);
2745 break;
2746 case MP_CMD_QUIT:
2747 exit_player_with_rc(MSGTR_Exit_quit, (cmd->nargs > 0)?cmd->args[0].v.i : 0);
2748 break;
2749 }
2750
2751 mp_cmd_free(cmd);

首先从等待命令,注意,如果不处于idle 模式下,必须带播放的文件名或者URL ,否则mplayer 直接退出。

往下看到stream流部分播放:

2853 current_module="open_stream";
2854 mpctx->stream=open_stream(filename,0,&mpctx->file_format);
2855 if(!mpctx->stream) { //error...
2856 mpctx->eof =libmpdemux_was_interrupted(PT_NEXT_ENTRY);
2857 goto goto_next_file;
2858 }
2859 inited_flags|=INITED_STREAM;

调用open_stream获取文件名filename对应的流。

33 stream_t* open_stream(char*filename,char** options, int* file_format)
{
… ...
44 //============ Open STDIN or plainFILE ============
45
46 returnopen_stream_full(filename,STREAM_READ,options,file_format);
47 }
进入open_stream_full:
208 stream_t* open_stream_full(char*filename,int mode, char** options, int* file_format) {
209 int i,j,l,r;
210 stream_info_t* sinfo;
211 stream_t* s;
212
213 for(i = 0 ; auto_open_streams[i]; i++) {
214 sinfo = auto_open_streams[i];
215 if(!sinfo->protocols) {
216 mp_msg(MSGT_OPEN,MSGL_WARN,"Stream type %s has protocols == NULL, it's a bug\n",sinfo->name);
217 continue;
218 }
219 for(j = 0 ; sinfo->protocols[j]; j++) {
220 l =strlen(sinfo->protocols[j]);
221 // l == 0 => Don't doprotocol matching (ie network and filenames)
222 if((l == 0 &&!strstr(filename, "://")) ||
223 ((strncmp(sinfo->protocols[j],filename,l) == 0) &&
224 (strncmp("://",filename+l,3) == 0))) {
225 *file_format =DEMUXER_TYPE_UNKNOWN;
226 s =open_stream_plugin(sinfo,filename,mode,options,file_format,&r);
227 if(s) return s;
228 if(r != STREAM_UNSUPPORTED) {
229 mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_FailedToOpen,filename);
230 return NULL;
231 }
232 break;
233 }
234 }
235 }
236

237 mp_msg(MSGT_OPEN,MSGL_ERR, "Nostream found to handle url %s\n",filename);
238 return NULL;
239 }

这个函数大概的意思是遍历auto_open_streams这个流数组,判断各个流对象sinfo所描述的协议是否符合filename所用的协议,如果符合,调用open_stream_plugin。从字面的意思来看,就是调用相应的插件。

再来看open_stream_plugin

147 stream_t* open_stream_plugin(stream_info_t* sinfo,char* filename,int mode,

148                  char** options, int* file_format, int* ret) {

	… …
176   	s = new_stream(-2,-2);
177   	s->url=strdup(filename);
178   	s->flags |= mode;
179   	*ret = sinfo->open(s,mode,arg,file_format);

180   	if((*ret) != STREAM_OK) {

181     		free(s->url);

182     		free(s);

183     		return NULL;

184   	}

192   	s->mode = mode;

193 

194 	mp_msg(MSGT_OPEN,MSGL_V, "STREAM: [%s] %s\n",sinfo->name,filename);

195   	mp_msg(MSGT_OPEN,MSGL_V, "STREAM: Description: %s\n",sinfo->info);

196   	mp_msg(MSGT_OPEN,MSGL_V, "STREAM: Author: %s\n", sinfo->author);

197   	mp_msg(MSGT_OPEN,MSGL_V, "STREAM: Comment: %s\n", sinfo->comment);

	… ...
}

首先看建立一个stream对象s= new_stream(-2,-2); 

412 stream_t* new_stream(int fd,int type){
413   stream_t *s=malloc(sizeof(stream_t));
414   if(s==NULL) return NULL;
415   memset(s,0,sizeof(stream_t));
416 
417 #ifdef HAVE_WINSOCK2
418   {
419     WSADATA wsdata; 
420     int temp = WSAStartup(0x0202, &wsdata); // there might be a better place for this (-> later)
421     mp_msg(MSGT_STREAM,MSGL_V,"WINSOCK2 init: %i\n", temp);
422   }
423 #endif
424   
425   s->fd=fd;
426   s->type=type;
427   s->buf_pos=s->buf_len=0;
428   s->start_pos=s->end_pos=0;
429   s->priv=NULL;
430   s->url=NULL;
431   s->cache_pid=0;
432   stream_reset(s);
433   return s;
434 }

主要是生成一个stream对象,并对其进行初始化,留意stream的成员变量,以后还会遇到的。

其中第431行的s->cache_pid成员保存cache进程的id

继续往前看,第179行调用流对象sinfoopen函数,一旦返回成功,则表示STREAM_OK

这样,关键的地方就是sinfo的结构体了。在stream/stream.c里找到auto_open_streams流对象数组:

91 stream_info_t* auto_open_streams[]= {
92 #ifdef HAVE_VCD
93 &stream_info_vcd,
94 #endif
95 #ifdef HAVE_CDDA
96 &stream_info_cdda,
97 #endif
98 #ifdef MPLAYER_NETWORK
99 &stream_info_netstream,
100 &stream_info_http1,
101 &stream_info_asf,
102 &stream_info_pnm,
103 &stream_info_rtsp,
104 #ifdef STREAMING_LIVE555
105 &stream_info_sdp,
106 &stream_info_rtsp_sip,
107 #endif
108 &stream_info_rtp,
109 &stream_info_udp,
110 &stream_info_http2,
111 #endif
112 #ifdef HAS_DVBIN_SUPPORT
113 &stream_info_dvb,
114 #endif
115 #ifdef USE_TV
116 &stream_info_tv,
117 #endif
118 #ifdef USE_RADIO
119 &stream_info_radio,
120 #endif
121 #ifdef HAVE_PVR
122 &stream_info_pvr,
123 #endif
124 #ifdef HAVE_FTP
125 &stream_info_ftp,
126 #endif
127 #ifdef HAVE_VSTREAM
128 &stream_info_vstream,
129 #endif
130 #ifdef LIBSMBCLIENT
131 &stream_info_smb,
132 #endif
133 &stream_info_cue,
134 #ifdef USE_DVDREAD
135 &stream_info_dvd,
136 #endif
137 #ifdef USE_DVDNAV
138 &stream_info_dvdnav,
139 #endif
140
141 &stream_info_null,
142 &stream_info_mf,
143 &stream_info_file,
144 NULL
145 };

可以看到在这个数组中,有很多流对象,有些流对象是通过宏定义来决定是否编译,也就是说可以通过配置来增加或者删除某些流对象。其中,每个流对象实际上就是对应一个插件。这里,我们的filenamemms开始的地址,对应stream_info_asf这个对象(通过protocal来决定的)。

873 stream_info_t stream_info_asf = {
874 "mms and mms over httpstreaming",
875 "null",
876 "Bertrand, ReimarDoeffinger, Albeu",
877 "originally based on work byMajormms (is that code still there?)",
878 open_s,
879 {"mms", "mmsu","mmst", "http", "http_proxy","mmshttp", NULL},
880 NULL,
881 0 // Urls are an option string
882 };

从上面的结构体可以看到该流对象的open函数是open_s

841 static int open_s(stream_t*stream,int mode, void* opts, int* file_format) {
842 URL_t *url;
843
844 stream->streaming_ctrl =streaming_ctrl_new();
845 if(stream->streaming_ctrl==NULL ) {
846 return STREAM_ERROR;
847 }
848 stream->streaming_ctrl->bandwidth = network_bandwidth;
849 url = url_new(stream->url);
850 stream->streaming_ctrl->url= check4proxies(url);
851 url_free(url);
852
853 mp_msg(MSGT_OPEN, MSGL_INFO,MSGTR_MPDEMUX_ASF_InfoStreamASFURL, stream->url);
854 if((!strncmp(stream->url,"http", 4)) && (*file_format!=DEMUXER_TYPE_ASF &&*file_format!=DEMUXER_TYPE_UNKNOWN)) {
855 streaming_ctrl_free(stream->streaming_ctrl);
856 stream->streaming_ctrl =NULL;
857 return STREAM_UNSUPPORTED;
858 }
859
860 if(asf_streaming_start(stream,file_format) < 0) {
861 mp_msg(MSGT_OPEN, MSGL_ERR,MSGTR_MPDEMUX_ASF_StreamingFailed);
862 streaming_ctrl_free(stream->streaming_ctrl);
863 stream->streaming_ctrl =NULL;
864 return STREAM_UNSUPPORTED;
865 }
866
867 *file_format =DEMUXER_TYPE_ASF;
868 stream->type =STREAMTYPE_STREAM;
869 fixup_network_stream_cache(stream);
870 return STREAM_OK;
871 }

844行的stream->streaming_ctrl= streaming_ctrl_new();

建立了一个流控制结构,注意这个结构体:

 38 typedef struct streaming_control {
 39     URL_t *url;
 40     streaming_status status;
 41     int buffering;  // boolean   
 42     unsigned int prebuffer_size; 
 43     char *buffer;
 44     unsigned int buffer_size;    
 45     unsigned int buffer_pos;
 46     unsigned int bandwidth; // The downstream available
 47     int (*streaming_read)( int fd, char *buffer, int buffer_size, struct streaming_control *stream_ctrl );
 48     int (*streaming_seek)( int fd, off_t pos, struct streaming_control *stream_ctrl );
 49     void *data;         
 50 } streaming_ctrl_t;

从结构成员来看,这个结构体就是用来控制读流数据的,以及一些网络参数。

再继续往下看:asf_streaming_start(stream,file_format),顾名思义,就是开始streaming。

50 static int asf_streaming_start(stream_t *stream, int *demuxer_type) {
… ...
65 //Is protocol mms or mmst?
66 if (!strcasecmp(proto, "mmst")|| !strcasecmp(proto, "mms"))
67 {
68 mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/TCP...\n");
69 fd =asf_mmst_streaming_start( stream );
70 stream->streaming_ctrl->url->port = port;
71 if( fd>-1 ) return fd;
72 mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/TCP failed\n");
73 if( fd==-2 ) return -1;
74 }
… ...
90 }

调用asf_mmst_streaming_start,得到fd,初步判断,fd就是一个网络流的句柄。

543 intasf_mmst_streaming_start(stream_t *stream)
544 {
… ...
574 if( url1->port==0 ) {
575 url1->port=1755;
576 }
577 s = connect2Server(url1->hostname, url1->port, 1);
578 if( s<0 ) {
579 mp_msg(MSGT_NETWORK,MSGL_INFO,"Net Down! Please Check theNetwork\n");
580 free(path);
581 return s;
582 }

583 mp_msg(MSGT_NETWORK,MSGL_INFO,MSGTR_MPDEMUX_MMST_Connected);
… ...
716 }

connect2Server,连接服务器:

int connect2Server(char *host, int port, int verb) {
… ...
return connect2Server_with_af(host,port, AF_INET,verb);
}
61 int connect2Server_with_af(char*host, int port, int af,int verb) {
… ...
86 socket_server_fd = socket(af,SOCK_STREAM, 0);
… …
182 if( connect( socket_server_fd,(struct sockaddr*)&server_address, server_address_size )==-1 ) {
184 if( errno!=EINPROGRESS ) {
186 if( (WSAGetLastError() !=WSAEINPROGRESS) && (WSAGetLastError() != WSAEWOULDBLOCK)) {
189 closesocket(socket_server_fd);
190 return TCP_ERROR_PORT;
191 }
192 }
194 tv.tv_sec = 0;
195 tv.tv_usec = 500000;
196 FD_ZERO( &set );
197 FD_SET( socket_server_fd, &set);
198 // When the connection will bemade, we will have a writeable fd
199 while((ret =select(socket_server_fd+1, NULL, &set, NULL, &tv)) == 0) {
200 mp_msg(MSGT_OPEN,MSGL_WARN, "select server timeout...\n");
201 if(count > 30 ||mp_input_check_interrupt(500)) {
202 if(count > 30)
203 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_NW_ConnTimeout);
204 else
205 mp_msg(MSGT_NETWORK,MSGL_V,"Connection interrupted by user\n");
206 return TCP_ERROR_TIMEOUT;
207 }
208 count++;
209 FD_ZERO( &set );
210 FD_SET( socket_server_fd,&set );
211 tv.tv_sec = 0;
212 tv.tv_usec = 500000;
213 }
… ...
235 return socket_server_fd;
236 }

连接流媒体服务器,等待服务器应答。Select超时为0.5s,总超时为30*0.5s=15s。当时在这个过程中也可以打断等待,通过mp_input_check_interrupt(500)

1800 int
1801 mp_input_check_interrupt(int time){
1802 mp_cmd_t* cmd;
1803 if((cmd =mp_input_get_cmd(time,0,1)) == NULL)
1804 return 0;
1805
1806 // mp_msg(MSGT_OPEN, MSGL_WARN,"cmd->id=%d\n",cmd->id);
1807
1808 switch(cmd->id) {
1809 case MP_CMD_QUIT:
1810 case MP_CMD_PLAY_TREE_STEP:
1811 case MP_CMD_PLAY_TREE_UP_STEP:
1812 case MP_CMD_PLAY_ALT_SRC_STEP:
1814 case MP_CMD_LOADFILE:
1815 case MP_CMD_LOADLIST:
1816
1817 // The cmd will be executedwhen we are back in the main loop
1818 return 1;
1819 }
1820 // remove the cmd from thequeue
1821 cmd =mp_input_get_cmd(time,0,0);
1822 mp_cmd_free(cmd);
1823 return 0;
1824 }

从上面的函数可以看到,mplayer只要接收到case列表中的命令,就可以打断等待服务器这一过程。通常对于没有回应的操作来说,直接打断并开始新的连接是非常有用的。

回到asf_mmst_streaming_start(stream_t*stream)这个函数,服务器连接成功后,向服务器发送请求,根据流的协议:

544 int asf_mmst_streaming_start(stream_t *stream)
545 {
	… …
578   s = connect2Server( url1->hostname, url1->port, 1);
	… …
588   /*
589   * Send the initial connect info including player version no. Client GUID (random) and the host 	address being connected to. 
590   * This command is sent at the very start of protocol initiation. It sends local information to the 	serve 
591   * cmd 1 0x01 
592   * */
593 
594   /* prepare for the url encoding conversion */
595 #ifdef USE_ICONV
596 #ifdef USE_LANGINFO
597   url_conv = iconv_open("UTF-16LE",nl_langinfo(CODESET));
598 #else
599   url_conv = iconv_open("UTF-16LE", NULL);
600 #endif
601 #endif
602 
603   snprintf (str, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1->hostname);
604   string_utf16 (data, str, strlen(str));
605 // send_command(s, commandno ....)
606   send_command (s, 1, 0, 0x0004000b, strlen(str)*2+2, data);
607 
608   len = recv (s, data, BUF_SIZE, 0) ;
608   len = recv (s, data, BUF_SIZE, 0) ;
609 
610   /*This sends details of the local machine IP address to a Funnel system at the server. 
611   * Also, the TCP or UDP transport selection is sent.
612   *
613   * here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
614   * and 1037 is the  local TCP or UDP socket number
615   * cmd 2 0x02
616   *  */
617 
618   string_utf16 (&data[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
619   memset (data, 0, 8);
620   send_command (s, 2, 0, 0, 24*2+10, data);
621 
622   len = recv (s, data, BUF_SIZE, 0) ;
623 
624   /* This command sends file path (at server) and file name request to the server.
625   * 0x5 */
626 
627   string_utf16 (&data[8], path, strlen(path));
628   memset (data, 0, 8);
629   send_command (s, 5, 0, 0, strlen(path)*2+10, data);
630   free(path);
631 
632   get_answer (s);
633 
634   /* The ASF header chunk request. Includes ?session' variable for pre header value. 
635   * After this command is sent, 
636   * the server replies with 0x11 command and then the header chunk with header data follows.
637   * 0x15 */
638 
639   memset (data, 0, 40);
640   data[32] = 2;
641 
642   send_command (s, 0x15, 1, 0, 40, data);
643 
644   num_stream_ids = 0;
645   /* get_headers(s, asf_header);  */
646 
647   asf_header_len = get_header (s, asf_header, stream->streaming_ctrl);
648 //  mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
649   if (asf_header_len==0) { //error reading header
650     closesocket(s);
651     return -1;
652   }
653   packet_length = interp_header (asf_header, asf_header_len);
654 
655 
656   /* 
657   * This command is the media stream MBR selector. Switches are always 6 bytes in length.  
658   * After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02]. 
659   * Where:  
660   * [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files. 
661   * It is not yet understood what all this means. 
662   * And the last [02] byte is probably the header ?session' value. 
663   *  
664   *  0x33 */
665 
666   memset (data, 0, 40);
667 
668   if (audio_id > 0) {
669     data[2] = 0xFF;
670     data[3] = 0xFF;
671     data[4] = audio_id;
672     send_command(s, 0x33, num_stream_ids, 0xFFFF | audio_id << 16, 8, data);
673   } else {
674   for (i=1; i<num_stream_ids; i++) {
675     data [ (i-1) * 6 + 2 ] = 0xFF;
676     data [ (i-1) * 6 + 3 ] = 0xFF;
677     data [ (i-1) * 6 + 4 ] = stream_ids[i];
678     data [ (i-1) * 6 + 5 ] = 0x00;
679   }
680 
681   send_command (s, 0x33, num_stream_ids, 0xFFFF | stream_ids[0] << 16, (num_stream_ids-1)*6+2 , data);
682   }
683 
684   get_answer (s);
685 
686   /* Start sending file from packet xx. 
687   * This command is also used for resume downloads or requesting a lost packet. 
688   * Also used for seeking by sending a play point value which seeks to the media time point. 
689   * Includes ?session' value in pre header and the maximum media stream time. 
690   * 0x07 */
691 
692   memset (data, 0, 40);
693 
694   for (i=8; i<16; i++)
695     data[i] = 0xFF;
696 
697   data[20] = 0x04;
698 
699   send_command (s, 0x07, 1, 0xFFFF | stream_ids[0] << 16, 24, data);
700 
701   stream->fd = s;
702   stream->streaming_ctrl->streaming_read = asf_mmst_streaming_read;
703   stream->streaming_ctrl->streaming_seek = asf_mmst_streaming_seek;
704   stream->streaming_ctrl->buffering = 1;
705   stream->streaming_ctrl->status = streaming_playing_e;
706 
707   packet_length1 = packet_length;
708   mp_msg(MSGT_NETWORK,MSGL_INFO,"mmst packet_length = %d\n", packet_length);
710 
711 #ifdef USE_ICONV
712   if (url_conv != (iconv_t)(-1))
713     iconv_close(url_conv);
714 #endif
715 
716   return 0;
717 }

上述代码片段,主要是请求mms流的一些通信。注意以下的几个赋值:

701 stream->fd = s;

702 stream->streaming_ctrl->streaming_read =asf_mmst_streaming_read;

703 stream->streaming_ctrl->streaming_seek =asf_mmst_streaming_seek;

704 stream->streaming_ctrl->buffering= 1;

705 stream->streaming_ctrl->status= streaming_playing_e;

701行把连接网络成功返回的网络句柄赋给stream结构体。接下来的是streaming_ctrl成员的赋值,asf_mmst_streaming_readasf_mmst_streaming_seek可以在stream/asf_mmst_streaming.c找到。这两个函数就是直接的网络通信,播放流的时候,会调用它们来实现读取网络流数据。



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值