实验目的
本次实验室在MFC环境下使用socket制作的应用程序,实现对RTSP与RTP协议的解析并播放缓存的媒体流。实现一边下载一边播放的音乐播放器。客户端使用RTSP协议与LIVE555服务器进行通信,如果与服务器的通信无误就启动RTP线程开始缓存文件并进行播放。本次程序设计还包括一些其他功能:
1使用MFC进行界面的设计
2使用RTP将收到文件下载到本地,进行播放
3 播放控制,包括暂停,播放,终止,快进,快退,拖动播放时间播放,音量控制,播放时间条随播放时间变化。
实验背景
1 RTSP协议
RTSP(Real Time Streaming Protocol),实时流协议,是一种流媒体
控制协议。。RTSP是一个多媒体播放控制协议,默认使用554或8554端口,用来使用户在播放从因特网下载的实时数据时能够进行控制,即控制实时数据的传输。RTSP 仅仅是使媒体播放器能控制多媒体流的传送。也就是说,RTSP只用于控制媒体流的传输。尽管有时可以把RTSP控制信息和媒体数据流交织在一起传送,但一般情况RTSP本身并不用于转送媒体流数据。媒体数据的传送可通过RTP/RTCP等协议来完成。
本实验中RTSP基于Socket连接,在建立Socket连接后,发送RTSP命令来
获取所请求的文件。RTP协议用来接收传来的文件数据。
RTSP协议有两种,一种是请求消息(request),一是回应消息(response),两种消息的格式不同。
请求格式 响应格式
其中URL是请求的地址,有两种:
一般是rtsp://192.168.0.1:(你的服务器的地址)/视频流的名字
如果地址有域名类似格式rtsp://computing.cuc.edu.cn/Angel.mp3
首部行用来加入交互命令,如下是简易的交互过程。
交互详细信息:
OPTION, 目的是得到服务器提供的可用方法
请求:
OPTIONSrtsp://computing.cuc.edu.cn/Angel.mp3 RTSP/1.0
CSeq: 1 //每个消息都有序号来标记,第一个包通常是option请求消息
User-Agent: VLC media player (LIVE555 Streaming Mediav2016.01.05)
响应:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 1 //每个回应消息的cseq数值和请求消息的cseq相对应
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE,SCALE,
GET_PARAMETER //服务器提供的可用的方法
DESCRIBE,为了得到会话描述信息(SDP)
请求:
DESCRIBE rtsp://computing.cuc.edu.cn/Angel.mp3RTSP/1.0
CSeq: 2
Accept: application/sdp
User-Agent: VLCmedia player (LIVE555 Streaming Media v2016.01.05)
响应:
RTSP/1.0 200 OK
Cseq: 2
Content-Length: 367
Content-Type: application/sdp
//以下都是sdp信息
a=range:npt=0-
SETUP,客户端提醒服务器建立会话,并确定传输模式
请求:
SETUP rtsp://computing.cuc.edu.cn/Angel.mp3/trackID=0RTSP/1.0
CSeq: 3
Transport:RTP/AVP/TCP;unicast;interleaved=0-1
User-Agent: VLCmedia player (LIVE555 Streaming Media v2016.01.05)
响应:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 3
Session: 6310936469860791894 //服务器回应的会话标识符
Cache-Control: no-cache
Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=6B8B4567
PLAY,客户端发送播放请求
请求:
PLAYrtsp://computing.cuc.edu.cn/Angel.mp3 RTSP/1.0
CSeq: 4
Session: 6310936469860791894
Range: npt=0.000- //设置播放时间的范围
User-Agent: VLC mediaplayer (LIVE555 Streaming Media v2016.01.05)
响应:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 4
Session: 6310936469860791894
Range: npt=0.000000-
RTP-Info: url=trackID=0;seq=17040;rtptime=1467265309
TEARDOWN,客户端发起关闭请求
请求:
TEARDOWN rtsp://computing.cuc.edu.cn/Angel.mp3RTSP/1.0
CSeq: 5
Session: 6310936469860791894
User-Agent: VLCmedia player (LIVE555 Streaming Media v2016.01.05)
响应:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 5
Session: 6310936469860791894
Connection: Close
本实验中,RTP使用了jrtplib插件来接收数据。JRTPLIB 是一个用C++语言实现的RTP库,包括UDP通讯
2 VLC
VLC多媒体播放器(最初命名为VideoLAN客户端)是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影音光盘及各类流式协议。它也能作为unicast或 multicast的流式服务器在IPv4或 IPv6的高速网络连接下使用。它融合了FFmpeg计划的解码器与libdvdcss程序库使其有播放多媒体文件及加密DVD影碟的功能。
本实验中通过VLC的函数来播放音乐,并控制音乐的一些基本操作。
vlc一些基本方法:
libvlc_new():创建libvlc_instance_t。
libvlc_media_new_path()://通过文件路径创建一个媒体实例
libvlc_media_player_new_from_media()://创建VLC媒体播放器
libvlc_media_player_release():释放libvlc_media_player_t
libvlc_media_release():释放libvlc_media_t。
libvlc_release():释放libvlc_instance_t
libvlc_media_player_play():播放。
libvlc_media_player_pause():暂停。
libvlc_media_player_stop():停止。
本项目用到的开发平台和程序库主要有:
l Operating System: Microsoft Windows
l Programming Language: C++
l Development Platform: Microsoft Visual Studio 2010
winsock2
本次实验主要流程:
建立Socket连接->启动RTSP线程->启动RTP线程->启动播放线程->释放资源
建立Socket:
启动RTSP线程:
p->rtsp = RTSP(p->rtspURI.getURI(),&socket); // 创建RTSP报文类实例
//向服务器发送rtsp请求
p->rtsp.sendOption();
p->rtsp.recvOption();
p->rtsp.sendDescribe();
p->rtsp.recvDescribe();
p->rtsp.sendSetup(RTPPORT);
p->rtsp.recvSetup();
p->rtsp.sendPlay();
p->rtsp.recvPlay();
//开启RTP线程
Sleep(1000);
AfxBeginThread(RtpThread,p, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
//RTSP不响应会断开
while (true)
{
Sleep(10000); // 每隔10秒钟,对线路进行一次保活操作
if(p->download) // 如果播放器还在下载
{
p->rtsp.sendParameter(); // 保活线路
p->rtsp.recvParameter();
}
else
{
p->MessageBox("文件传输完成!");
break;
}
}
RTP线程:
int length = socket.RecvUdp(data, sizeof(data)); // 利用UDP协议接收传输的数据
启动播放线程:
vlc =libvlc_new(vlc_argc, vlc_args);
media =libvlc_media_new_path(vlc, pMediaPathName); //通过文件路径创建一个媒体实例
player =libvlc_media_player_new_from_media(media); //创建VLC媒体播放器
libvlc_media_parse(media); //解析媒体实例
libvlc_media_player_set_media(player,media);
libvlc_media_player_play(player);
程序类图:
BlockSocket控制Socket连接方法
RTSP发送rtsp消息
URI用来解析输入的url地址,检测文件名正确性,获取文件名,端口
VlcCtrl控制播放媒体实例
CRTSPClientMFC控制主线程两个版本,一个Win32控制台程序,一个MFC程序
Win32控制台程序:
1. 启动程序输入连接的地址,输错会打印错误信息,正确则利用socket获取主机地址,并打印出来,
然后自动开启RTSP线程和RTP线程,等待几秒后,开始播放
播放过程中,按程序提醒可以测试音量功能,播放暂停功能,快进快退功能。
MFC程序:
1. 程序主界面
程序开始,没有下载直接点播放按钮,会弹出如下错误
输入地址错误,则弹出格式错误。
停止键从头来播放,时间滚动条随播放时间增加,开始音量没有设置为0,拖动音量才有效果
|
问题:
Win32控制台中:
1、 使用strcmp("rtsp://",this->uri.substr(0,7).c_str())作比较时,开始没有加上c_str()则报错,原因是基本的string类不能和char*比较,而c_str()作用是c_str() 以 char* 形式传回 string内含字符串。
2、 字符串相加不能存在int型,需要转换,string option = "OPTIONS" + uri.getURI() +" RTSP/1.0\r\nCSeq: " + CSeq + "\r\n" + userAgent+"\r\n";
使用itoa函数char *itoa( int value, char *string,intradix); value:欲转换的数据。 string:目标字符串的地址。radix:转换后的进制数,可以是10进制、16进制等。atoi是把字符串转换成int型。
3、 开始写RTSP请求时,末尾只写了一个“\r\n”,结果收不到包,解决办法应该是写”\r\n\r\n”。
4、 环境配置出错,运行后出现mainlibvlc error:no plugins found !check your vlc installation错误,解决办法把libvlc.dll, libvlccore.lib和plugins文件放在DEBUG目录下。
5、 播放音乐时不出声音,由于RTP还没有获取传输文件,就开始播放,解决办法是在开启播放线程时先等几秒,然后再播放。
MFC中问题:
1、 上面的第五个问题,在MFC中Sleep几秒钟会延迟,主要表示是下面的列表信息等了几秒才显示,然后发现不用Sleep也能正常播放,说明在MFC中线程的处理比win32快。
2、 由于使用线程的时候没有用信号量控制,导致在写播放线程后,一些例如快进,播放等操作会比线程执行的更快,产生内存泄露,用了一个变量控制,写的不是很好。
3、 调节音量的地方有两个问题,一个是静态文本控件如何改变数值,一个就是使用滑动条拖动时libvlc_audio_set_volume(player, volume)报错,询问了一些人遇到了这个问题,可能是因为程序开始会执行这个函数,所以出错,加变量控制把第一次执行跳过就好了。
未解决问题:
由于时间和能力有限,程序还有一些未解决的问题。
播放时间拖动条的功能只实现了随播放时间变化,拖动到指定地方进行播放功能没有实现。
RTP在传数据时有时会有错误,出现杂音,Win32版本中出现的error就是这个原因
下载速度如果没有播放速度快,播放就会停止,没有继续缓存
写线程没有用事件控制,后期调试时,使用了很多变量控制,对程序的可读性和封装造成了一定影响。
下面列出了实现系统功能的关键代码段。
1. URI地址分析
/*一般是rtsp://192.168.0.1:(你的服务器的地址)/视频流的名字
如果地址有域名类似格式rtsp://computing.cuc.edu.cn/Angel.mp3
采用默认的端口
*/
2. //判断地址是否正确
3. bool URI::uriIsCorrect()
4. {
5. //先检查头部
6. if(strcmp("rtsp://",uri.substr(0,7).c_str())!=0){
7. return false;
8. }
9. else
10. {
11. if(strcmp("",this->getFilename().c_str())!=0)
12. {
13. return true;
14. }
15. else
16. return false;
17. }
18. }
19.
20. //获取主机地址
21. string URI::getHostname()
22. {
23. int startPos = strlen("rstp://");
24. int tempPos = uri.find(":",startPos);
25. if(tempPos == string::npos)
26. {
27. tempPos= uri.find("/",startPos);
28. }
29. stringm_hostname = uri.substr(startPos,tempPos-startPos);
30. if (isalpha(m_hostname[0]))
31. {
32. //使用gethostname获取主机名
33. WSADATAwsaData;
34. if (WSAStartup(MAKEWORD(2,2), &wsaData)!= 0)
35. {
36. cout<< "WSAStartup failed"<< endl;
37. return NULL;
38. }
39. hostent*remoteHost = gethostbyname(m_hostname.c_str());
40. if (remoteHost != NULL)
41. {
42. int i = 0;
43. in_addraddr;
44. while (remoteHost->h_addr_list[i] != 0)
45. {
46. addr.S_un.S_addr= *(u_long *)remoteHost->h_addr_list[i++];
47. return inet_ntoa(addr);
48. }
49. }
50. WSACleanup();
51. return NULL;
52. }
53. return m_hostname;
54. }
55.
56. //获取端口号
57. string URI::getPort()
58. {
59. int startPos = strlen("rtsp://");
60. int tempPos = uri.find(":",startPos);
61. if(tempPos == string::npos)
62. {
63. return "554";
64. }
65. else
66. {
67. int endPos = uri.find("/",startPos);
68. return uri.substr(tempPos,endPos-tempPos);
69. }
70. }
71.
72. //获取文件名
73. string URI::getFilename()
74. {
75. int startPos=strlen("rtsp://");
76. int tempPos = uri.find("/",startPos);
77.
78. if(tempPos == string::npos) //如果没有找到
79. {
80. return "";
81. }
82. else //返回文件名
83. {
84. int endPos = uri.length()-1;
85. return uri.substr(tempPos+1,endPos-tempPos);
86. }
87. }
88.RTSP线程
89. RTSP::RTSP(string uri,BlockSocket *blockSocket)
90. {
91. this->uri = URI(uri);
92. this->blockSocket = blockSocket;
93. CSeq = 0;
94. userAgent ="User-Agent: VLC media player (LIVE555Streaming Media v2016.01.05)";
95. m_totalTime= 0;
96.
97. }
98.
99. int RTSP::getTotalTime()
100. {
101. return this->m_totalTime;
102. }
103.
104.
105. int RTSP::sendOption() //OPTION请求
106. {
107. CSeq++;
108. char c[5];
109. itoa(CSeq,c,10);
110. stringoption = "OPTIONS " + uri.getURI()+" RTSP/1.0\r\nCSeq: " + c + "\r\n" + userAgent + "\r\n\r\n";
111. if(blockSocket->Send((char*)option.c_str(),option.length()))
112. {
113. cout<<"OPTIONS发送失败!"< 114. return 1; 115. } 116. return 0; 117. } 118. char *RTSP::recvOption() 119. { 120. memset(data,'\0',sizeof(data)); 121. if(blockSocket->Recv(data,sizeof(data))) 122. { 123. cout<<"OPSTIONS接受失败!"< 124. return ""; 125. } 126. return data; 127. } 128. int RTSP::sendDescribe() //DESCRIBE请求 129. { 130. CSeq++; 131. char c[5]; 132. itoa(CSeq,c,10); 133. stringdescribe = "DESCRIBE " +uri.getURI() +" RTSP/1.0\r\nCSeq: "+ c + "\r\nAccept: application/sdp\r\n"+ userAgent + "\r\n\r\n"; 134. if(blockSocket->Send((char*)describe.c_str(),describe.length())) 135. { 136. cout<<"DESCRIBE发送失败!"< 137. return 1; 138. } 139. 140. return 0; 141. } 142. char *RTSP::recvDescribe() 143. { 144. memset(data,'\0',sizeof(data)); 145. if(blockSocket->Recv(data,sizeof(data))) 146. { 147. cout<<"DESCRIBE接受失败!"< 148. return ""; 149. } 150. //发现文件时间长度 151. stringtimes(data); 152. int startPos = times.find("a=range:npt=0-")+14; 153. int endPos = times.find("\r\n",startPos); 154. stringm_time = times.substr(startPos,endPos-startPos); 155. this->m_totalTime = atoi(m_time.c_str())*1000; 156. //cout< 157. return data; 158. } 159. int RTSP::sendSetup(char*port) //SETUP请求 160. { 161. CSeq++; 162. char c[5]; 163. itoa(CSeq,c,10); 164. int port1 = atoi(port)+1; //指定RTP端口号 165. char mport[6]; 166. itoa(port1,mport, 10); 167. stringsetup = "SETUP " + uri.getURI() +" RTSP/1.0\r\nCSeq: " + c + "\r\n" + userAgent + "\r\nTransport: RTP/AVP;unicast;client_port="+ port +"-"+ mport +"\r\n\r\n"; 168. if(blockSocket->Send((char*)setup.c_str(),setup.length())) 169. { 170. cout<<"SETUP发送失败!"< 171. return 1; 172. } 173. return 0; 174. } 175. char *RTSP::recvSetup() 176. { 177. memset(data,'\0',sizeof(data)); 178. if(blockSocket->Recv(data,sizeof(data))) 179. { 180. cout<<"SETUP接受失败!"< 181. return ""; 182. } 183. stringsetupData(data); 184. int startPos = setupData.find("Session: ")+9; //还有一个空格 185. int temp=0; 186. while(data[startPos+temp]!=''&& data[startPos+temp]!='\r') 187. { 188. temp++; 189. } 190. char m_session[100]; 191. memset(m_session,'\0', 100); 192. memcpy(m_session,data+startPos, temp); 193. this->session = m_session; 194. return data; 195. } 196. int RTSP::sendPlay() //PLAY请求 197. { 198. CSeq++; 199. char c[5]; 200. itoa(CSeq,c,10); 201. stringplay = "PLAY " + uri.getURI() +" RTSP/1.0\r\nCSeq: " + c +"\r\n"+ userAgent+"\r\nSession: "+ this->session +"\r\nRange:npt=0.000-\r\n" +"\r\n\r\n"; 202. if(blockSocket->Send((char*)play.c_str(),play.length())) 203. { 204. cout<<"PLAY发送失败!"< 205. return 1; 206. } 207. return 0; 208. } 209. char *RTSP::recvPlay() 210. { 211. memset(data,'\0',sizeof(data)); 212. if(blockSocket->Recv(data,sizeof(data))) 213. { 214. cout<<"PLAY接受失败!"< 215. return ""; 216. } 217. return data; 218. } 219. 220. int RTSP::sendPause() 221. { 222. CSeq++; 223. char c[5]; 224. itoa(CSeq,c,10); 225. string pause= "PAUSE " + uri.getURI() +" RTSP/1.0\r\nCSeq: " + c + "\r\nSession: "+ this->session +"\r\n"+userAgent +"\r\n\r\n"; 226. if(blockSocket->Send((char*)pause.c_str(),pause.length())) 227. { 228. cout<<"PAUSE发送失败!"< 229. return 1; 230. } 231. return 0; 232. } 233. char *RTSP::recvPause() 234. { 235. memset(data,'\0',sizeof(data)); 236. if(blockSocket->Recv(data,sizeof(data))) 237. { 238. cout<<"PAUSE接受失败!"< 239. return ""; 240. } 241. return data; 242. } 243. int RTSP::sendTeardown() 244. { 245. CSeq++; 246. char c[5]; 247. itoa(CSeq,c,10); 248. stringteardown = "TEARDOWN " + uri.getURI()+" RTSP/1.0\r\nCSeq: " + c + "\r\nSession: "+ this->session +"\r\n"+userAgent +"\r\n\r\n"; 249. if(blockSocket->Send((char*)teardown.c_str(),teardown.length())) 250. { 251. cout<<"TEARDOWN发送失败!"< 252. return 1; 253. } 254. return 0; 255. } 256. char *RTSP::recvTeardown() 257. { 258. memset(data,'\0',sizeof(data)); 259. if(blockSocket->Recv(data,sizeof(data))) 260. { 261. cout<<"TEARDOWN接受失败!"< 262. return ""; 263. } 264. return data; 265. } 266. int RTSP::sendParameter() 267. { 268. CSeq++; 269. char c[5]; 270. itoa(CSeq,c,10); 271. stringparameter = "GET_PARAMETER " + uri.getURI()+" RTSP/1.0\r\nCSeq: " + c + "\r\nSession: "+ this->session +"\r\n"+userAgent +"\r\n\r\n"; 272. if(blockSocket->Send((char*)parameter.c_str(),parameter.length())) 273. { 274. cout<<"GET_PARAMETER发送失败!"< 275. return 1; 276. } 277. return 0; 278. } 279. char *RTSP::recvParameter() 280. { 281. memset(data,'\0',sizeof(data)); 282. if(blockSocket->Recv(data,sizeof(data))) 283. { 284. cout<<"GET_PARAMETER接受失败!"< 285. return ""; 286. } 287. return data; 288. } RTSP控制代码 UINTCRTSPClientMFCDlg::RtspThread(PVOID lpParam) { CRTSPClientMFCDlg *p = (CRTSPClientMFCDlg*)lpParam; BlockSocket socket = BlockSocket(); if(socket.Initialize()) { p->MessageBox("SOCKET打开失败!"); return0; } if(socket.HintsAndResult(p->rtspURI.getHostname().c_str(),p->rtspURI.getPort().c_str())) { p->MessageBox("获取地址失败!"); return0; } if(socket.Open()) { p->MessageBox("SCOKET创建失败!"); return0; } if(socket.Connect()) { p->MessageBox("SOCKET连接失败!"); return0; } p->rtsp = RTSP(p->rtspURI.getURI(),&socket); //创建RTSP报文实例 //向服务器发送RTSP请求 p->rtsp.sendOption(); p->rtsp.recvOption(); p->rtsp.sendDescribe(); p->rtsp.recvDescribe(); p->rtsp.sendSetup(RTPPORT); p->rtsp.recvSetup(); p->rtsp.sendPlay(); p->rtsp.recvPlay(); p->hasRTSP = true; //时间赋值 p->totalTime = p->rtsp.getTotalTime(); CString m_time; m_time.Format("[00:00:00/%02d:%02d:%02d]",p->totalTime/3600000,p->totalTime/60000,(p->totalTime/1000-(p->totalTime/60000*60))); p->strTime->SetWindowText(m_time); //开启RTP线程 Sleep(1000); p->m_listInfo.InsertItem(p->infoCount++,"RTSP请求成功开始传输数据..."); p->m_listInfo.InsertItem(p->infoCount++,"---------------------------------"); AfxBeginThread(RtpThread, p,THREAD_PRIORITY_NORMAL, 0, 0, NULL); //RTSP不响应会断开 while (true) { Sleep(10000); // 每隔10秒激活一次 if(p->download) // 如果播放器还在下载 { p->rtsp.sendParameter(); // 保活线路 p->rtsp.recvParameter(); } else { p->MessageBox("文件传输完成"); break; } } p->rtsp.sendTeardown(); p->rtsp.recvTeardown(); socket.Close(); socket.Cleanup(); return 0; } RTP控制线程: UINTCRTSPClientMFCDlg::RtpThread(PVOID lpParam) // RTP线程 { CRTSPClientMFCDlg *p = (CRTSPClientMFCDlg*)lpParam; BlockSocket socket = BlockSocket(); if(socket.Initialize()) { p->MessageBox("SOCKET打开连接失败!"); return0; } if(socket.BindUdp(RTPPORT)) { p->MessageBox("绑定UDP失败!"); return0; } //传输文件 fstream file; // 创建文件流 file.open(p->rtspURI.getFilename(),ios::out|ios::binary|ios::trunc); // 打开文件流 chardata[MAX_BUFF_SIZE]; memset(data, '\0',sizeof(data)); // GetLocalTime(×end); int length =socket.RecvUdp(data,sizeof(data)); // 利用UDP接收传输数据 // cout< int sum = 0; file.write(data+433, length-433); //第一次接收会有多余东西 sum += length - 433; while (true) { memset(data, '\0',sizeof(data)); length = socket.RecvUdp(data, sizeof(data)); if(length <= 0) { p->download = false; break; } else { file.write(data+16, length-16); sum += length - 16; } } return 0; } VLC控制代码: void VlcCtrl::init() { int vlc_argc= 0; char*vlc_args[100]; vlc_args[vlc_argc++] = "--ignore-config"; vlc = libvlc_new(vlc_argc, vlc_args); } void VlcCtrl::openMedia(constchar* pMediaPathName) { media = libvlc_media_new_path(vlc,pMediaPathName); //创建一个媒体实例 player = libvlc_media_player_new_from_media(media); //创建vlc播放器 libvlc_media_parse(media); //解析媒体实例 libvlc_media_player_set_media(player,media); } void VlcCtrl::pause() { if(player) { libvlc_media_player_pause (player); } } void VlcCtrl::play() { if(player) { libvlc_media_player_play (player); } } bool VlcCtrl::IsPlaying() { if (player) { return(1 == libvlc_media_player_is_playing(player)); } return false; } void VlcCtrl::stop() { if(player) { libvlc_media_player_stop (player); } } int64_tVlcCtrl::GetLength() { int64_t length =libvlc_media_player_get_length(player); returnlength; } void VlcCtrl::FastForward() { if (player) { libvlc_time_t time =libvlc_media_player_get_time(player) + 5000; int64_t allTime = GetLength(); if(time > allTime) { time = allTime; } libvlc_media_player_set_time(player,time); } } void VlcCtrl::BackBackward() { if (player) { libvlc_time_t time = libvlc_media_player_get_time(player)- 5000; int64_t allTime = GetLength(); if(time < 0) { time = 0; } libvlc_media_player_set_time(player,time); } } void VlcCtrl::SetVolume(intvolume) { libvlc_audio_set_volume(player, volume); } bool VlcCtrl::hasPlayer() { if(player) return true; else return false; } int64_tVlcCtrl::GetTime() { int64_t time =libvlc_media_player_get_time(player); returntime; } Play控制线程: UINTCRTSPClientMFCDlg::PlayThread(PVOID lpParam) // PLAY线程 { CRTSPClientMFCDlg *p = (CRTSPClientMFCDlg*)lpParam; p->vlcCtrl.init(); p->vlcCtrl.openMedia(p->rtspURI.getFilename().data()); p->hasPlayer = true; p->vlcCtrl.play(); while(1) { p->UpdatePosition(); Sleep(1000); } return 0; }