林老师最后一次大作业:Project 10 – RTSP Windows Client: RFC 2326
Section I Problem Specification
翻译老师要求:
RSTP的windows客户端:RFC 2326
A. Overview
Introduction of RTSP
The Real Time Streaming Protocol (RTSP, RFC 2326, http://www.cs.columbia.edu/~hgs/rtsp/) is a network control protocol designed for use in entertainment and communications systems to control streaming media servers. The protocol is used for establishing and controlling media sessions between end points. Clients of media servers issue VCR-like commands, such as play and pause, to facilitate real-time control of playback of media files from the server.
译:A.总览
1.介绍RTSP
实时流传输协议(RTSP,RFC 2326)是一种在娱乐和交流系统中被用于控制流媒体服务器的网络控制协议。该协议被用于在两个端点(译者:最好简单理解为一个播放MP3的播放器的客户端,和一个服务端)之间建立和控制媒体的交流。媒体的客户端能够发出类似于VCR的命令行,例如播放、暂停,因此有利于实时控制来自服务器的媒体文件的播放。
The transmission of streaming data itself is not a task of the RTSP protocol. Most RTSP servers use the Real-time Transport Protocol (RTP, RFC 3550, 3551, http://www.cs.columbia.edu/~hgs/rtp/) for media stream delivery; however some vendors implement proprietary transport protocols.
While similar in some ways to HTTP, RTSP defines control sequences useful in controlling multimedia playback. While HTTP is stateless, RTSP has state; an identifier is used when needed to track concurrent sessions. Like HTTP, RTSP uses TCP to maintain an end-to-end connection and, while most RTSP control messages are sent by the client to the server, some commands travel in the other direction (i.e. from server to client).
译:RTSP并不负责流数据的传输。大多数RTSP服务器使用实时传输协议(RTP, RFC 3550, 3551)传输流数据。然而大多数供应商也会考虑使用自己发明的协议。
某种意义上讲与HTTP协议相似,RTSP定义了有利于控制多媒体播放的控制序列。然后HTTP没有状态,而RTSP是有状态的(译者:RTSP协议在播放音乐时有播放、暂停的状态)。如果需要,会有一个标示符用于跟踪并发的会话。类似于HTTP,RTSP使用TCP维持端到端的联接,并且,当大多数RTSP控制消息被客户端发送至服务端的同时,也有一些命令从服务端发送到客户端。
Presented here are the basic RTSP requests. Some typical HTTP requests, like the OPTIONS request, are also available. The default transport layer port number is 554.
OPTIONS
An OPTIONS request returns the request types the server will accept.
DESCRIBE
A DESCRIBE request includes an RTSP URL (rtsp://...), and the type of reply data that can be handled. The default port for the RTSP protocol is 554 for both UDP and TCP transports. This reply includes the presentation description, typically in Session Description Protocol (SDP, RFC 4566) format. Among other things, the presentation description lists the media streams controlled with the aggregate URL. In the typical case, there is one media stream each for audio and video.
SETUP
A SETUP request specifies how a single media stream must be transported. This must be done before a PLAY request is sent. The request contains the media stream URL and a transport specifier. This specifier typically includes a local port for receiving RTP data (audio or video), and another for RTCP data (meta information). The server reply usually confirms the chosen parameters, and fills in the missing parts, such as the server's chosen ports. Each media stream must be configured using SETUP before an aggregate play request may be sent.
PLAY
A PLAY request will cause one or all media streams to be played. Play requests can be stacked by sending multiple PLAY requests. The URL may be the aggregate URL (to play all media streams), or a single media stream URL (to play only that stream). A range can be specified. If no range is specified, the stream is played from the beginning and plays to the end, or, if the stream is paused, it is resumed at the point it was paused.
PAUSE
A PAUSE request temporarily halts one or all media streams, so it can later be resumed with a PLAY request. The request contains an aggregate or media stream URL. A range parameter on a PAUSE request specifies when to pause. When the range parameter is omitted, the pause occurs immediately and indefinitely.
TEARDOWN
A TEARDOWN request is used to terminate the session. It stops all media streams and frees all session related data on the server.
GET_PARAMETER
The GET_PARAMETER request retrieves the value of a parameter of a presentation or stream specified in the URI. The content of the reply and response is left to the implementation. GET_PARAMETER with no entity body may be used to test client or server liveness ("ping").
译:下面列出来的都是基本的RTSP请求消息,另外一些典型的HTTP的请求消息,比如OPTIONS,也是可以使用的。默认的传输层的端口号是554.
OPTIONS:使用该请求消息,将会返回服务器将要接受的请求类型。
DESCRIBE:该请求消息包括一个RTSP的URL(rtsp://...),并且能够被处理的回应数据的类型。对于UDP和TCP的传输,RTSP默认的端口号是554。回应包括了会话描述协议格式(SDP, RFC 4566)的表示描述,此外,表示描述还列出了被聚合URL控制的媒体流。在典型的情况下,这里各有一个媒体流负责音频和视频。
SETUP:该请求消息详细说明了一个单独的媒体流将如何被传输。该消息必须先于PLAY消息发送。该消息应该包含媒体流的URL和一个传输的标示符。这个标示符应该包含一个本地的端口用于接收RTP的数据(包括音频或者视频),和另一个端口用于接收RTCP数据(元信息)。服务端作出回应时,通常会确认一些参数,并且填满遗失的部分,包括服务端的端口。每一个媒体流都必须确认已经在一个聚合的PLAY请求消息发送之前已经使用了SETUP请求消息
PLAY:该请求消息将使得一个或者多个媒体流被播放了。如果多个PLAY消息被发送了,将会使用栈的结构来处理它。URL也可以是一个聚合的URL(即是:播放所有的媒体流),或者一个单独的媒体流URL(只播放一个流)。播放范围也是可以指定的,如果没有指定范围,那么这个流将会被从头播放结尾,或者如果流被暂停了,将会在它暂停的地方开始播放。
PAUSE:该请求消息能够暂时暂停一个或者多个媒体流,所以这个暂停可以被之后的PLAY消息回复。这个请求包含一个聚合或者单独的媒体流URL。存在于PAUSE里面的参数可以指定何时暂停。如果不指定何时暂停,那么这个暂停将会立刻发生。
TEARDOWN:该请求消息将用于暂停这个会话,它将会暂停所有的媒体流,并且释放所有存在于服务器与会话相关的数据。
GET_PARAMETER:该请求消息可以得到一个表示描述或者以URL指定的流的所有参数的值。我们还可以指定回应的内容和反应。一个没有实体内容的GET_PARAMETER将会用于测试客户端或者服务器的是否活着(类似于ping)。
Introduction of LIVE555
We will use LIVE555 as the streaming server for testing your RTSP Windows client player.LIVE555 is an open source (LGPL) C++ library for multimedia streaming. This code forms a set of C++ libraries for multimedia streaming, using open standard protocols (RTP/RTCP, RTSP, SIP). The libraries are already being used to implement applications such as:
- "the LIVE555 Media Server" (a RTSP server application, http://www.live555.com/mediaServer/);
- "liveCaster"(http://www.live555.com/liveCaster/) and "playRTPMPEG" (http://www.live555.com/multikit/playRTPMPEG.html)(for streaming MP3 audio using RTP/RTCP); and "vobStreamer"(http://www.live555.com/vobStreamer/) (for streaming DVD content using RTP/RTCP/RTSP).
- The libraries can also be used to stream, receive, and process MPEG, H.264, H.263+, DV or JPEG video, and several audio codecs. They can easily be extended to support additional (audio and/or video) codecs, and can also be used to build basic RTSP or SIP clients and servers, and have been used to add streaming support to existing media player applications, such as "VLC"(http://www.videolan.org/vlc/) and "MPlayer"(http://www.live555.com/mplayer/).
译:我们将会使用LIVE5555作为流提供服务器用于测试你的基于RTSP协议的客户端播放器。LIVE5555一个开源(LGPL) 的C++库,主要用于多媒体流。代码都是一个C++库的集合,使用了开放的标准协议(RTP/RTCP, RTSP, SIP)服务多媒体流。使用该库实现的应用程序有如下:
- "the LIVE555 Media Server" (一个基于RTSP的服务端应用程序);
- "liveCaster"and "playRTPMPEG" (使用 RTP/RTCP协议播放MP3的音频);
- "vobStreamer"(使用RTP/RTCP/RTSP播放DVD流的内容).
这个库也可以用于处理流的接收,还有处理如下格式:MPEG, H.264, H.263+, DV, JPEG video以及其它的几种多媒体编码格式。该库可轻易的扩展,支持其他的音频或者视频的格式,当然也可以用于建立基本的RTSP或SIP的客户端或者服务端,并且还能被用于改善已存的媒体播放器,使其可以播放流,比如:VLC,MPlayer。
Specification
Your assignment is to write a RTSP client that should work with LIVE555 Media Server.
1. Your implementation should follow the specification in the Real Time Streaming Protocol (RTSP, RFC 2326, http://tools.ietf.org/html/rfc2326), so that your version of RTSP Client is able to work together with the LIVE555 MediaServer.
2. Your implementation should follow the specification in the Real-time Transport Protocol (RTP, RFC 3550, 3551) and RTP Payload Format for MPEG1/MPEG2 Video (RFC 2250), so that your version of RTSP Client is able to handle RTP packets correctly. You may achieve this by writing your own code or just using a third party library, such as:
The JRDPLIB is an open-source project for packetizing/de-packetizing video/audio data over RTP (http://research.edm.uhasselt.be/~jori/page/index.php?n=CS.Jrtplib).(The JRTPLIB has routines for RTCP, but RTCP is not needed in your implementation because you are not required to encode/decode the audio/video stream data dynamically.), you can also refer to a programming instruction(http://blog.csdn.net/blog51/article/details/2408531) in Chinese.
A tiny RTP library (http://icourse.cuc.edu.cn/networkprogramming/assignments/RTSP/70565912.blog.51cto.com/Rtsp.rar) introduced by http://70565912.blog.51cto.com.
译:
你的作业主要是写一个基于RTSP的客户端,这个客户端能够和LIVE 媒体服务器通信。
1、你在实现的过程中一定遵守实时流传输协议(RTSP, RFC 2326)的具体说明,如此你开发出来的客户端才可以与LIVE555媒体服务器通信。
2、你在实现的过程中一定遵守实时传输协议(RTP, RFC 3550, 3551)的具体说明,此外,RTP的有效载荷格式必须遵守MPEG1/MPEG2(RFC 2250)的具体说明。如此,你的RTSP客户端才能够正确处理RTP包。你可以完全自己写代码实现,当然也可以使用现存的类库,比如:
JRDPLIB 是一个开源项目,用于封装或者解码传输于RTP的视频或者音频数据。(JRTPLIB 经常使用RTCP,但是RTCP对我们的实现来说并不是必须的,因为我们并不要求动态解码或者编码视频或者音频流。当然,你也可以参考一些编码指导,中文的哟)一个小小的RTP库,其介绍请猛击此处。
Your newly developed application should be a MFC application on Windows platform, butyou are free tochoose any mechanisms for socket I/Omultiplexing (Blocking with Multi-threading, the select( ) system call, asynchronous programming with WSAAsyncSelect or any other models). It is important that you should implement the RTSP client to be independent of the LIVE555 libraries (that means using any source codes of LIVE555 in your implementation is not permitted.).
4. Your RTSP Client Must accomplish basic and advanced functions:
Basic function: Visit the on-demand file stream from the Live555MediaServer with URL:“rtsp://127.0.0.1:8554/mp3AudioTest” (Windows version) or “rtsp://127.0.0.1:554/test.mp3” (Linux version). The file “test.mp3” will be ready for your RTSP request. The basic task of your RTSP Client is that it can record the steam as a local media file.
l Advanced function: The advanced task of your RTSP Client is that it can play the media file (mp3 only) from the on-demand streaming server. You can do this by taking advantage of the libvlc-library, which is the core component of the VLC media player. An example can be found at http://www.codeproject.com/Articles/38952/VLCWrapper-A-Little-C-wrapper-Around-libvlc. Please refer to libvlc (http://www.videolan.org/vlc/libvlc.html) and find the libs on the VideoLan site (ftp://ftp.videolan.org/pub/videolan/vlc/2.0.4/win32/ ) in special ZIP files intended for developers. Note: You should use VLC to play mp3 only, please write your own code to interpret RTSP/RTP and stream down the mp3 file.
5. Implementations thatdo not use Object-Oriented Programming will not be accepted. That also means itis not acceptable towrite thisproject in a single function (or even just a couple of functions).
3、有一个RTSP客户端的例子(openRTSP),这是一个基于linux命令行的程序,可以用来打开流、接收流,以RTSP URL的格式来记录媒体流(这样的URL往往是这样的:rtsp://)
你自己开发的一个程序应该是基于windows平台的MFC框架下的应用程序,然而,你可以自由选择任何基于I/O多路复用的socket通信机制(这个要找老师确定一下),无论是使用多线程来阻塞,或者使用select()系统调用,使用异步的WSAAsyncSelect 和其他任何模式)。你必须独立于LIVE555库来实现你的RTSP客户端,也就是说不能调用LIVE555库中与通信相关的函数,或者使用LIVE555库中的代码。
4、 你的RTSP的客户端应该完成基本和高级的功能:
基本功能:按需使用URL访问Live555Media服务端的资源文件。在windows上,URL应该类似于:rtsp://127.0.0.1:8554/mp3AudioTest。在Linux上,应该类似于:rtsp://127.0.0.1:554/test.mp3,当然,在服务端肯定存在test.mp3的文件来满足客户端的请求。你完成的RTSP客户端的基本功能应该能后将传输过来的流,保存在本地。
高级功能:按需从服务端传过来的流中播放音频文件(只是MP3格式)。为了此项功能,你可以好好利用:libvlc库,这是VLC播放器的核心组件。一个例子可供您使用:猛击可得。请您尽可能的参考libvlc的说明,并且请到网站VideoLan下载专门供开发人员使用的zip压缩文件。注意:你只可以使用VLC去播放Mp3文件,但是请写出代码,用于完成RTSP/RTP的传输和下载流保存到本地。
5、必须使用面向对象的思维来完成代码的实现。当然,你不能将一个工程写一个单一的函数中(或者一大堆函数)。
Notes:
You can download the live555 on the course website. There are two versions (Linux @ http://icourse.cuc.edu.cn/networkprogramming/assignments/RTSP/live-linux.rar and Windows@ http://icourse.cuc.edu.cn/networkprogramming/assignments/RTSP/live-wondows.rar ), both include source and executable files. You can use the source code to analyze the RTSP protocol, and run the executable file to test your client.
If you choose the Linux version, you can find the server (live555MediaServer) under “live/mediaServer/”, and if you choose the Windows version, you can find the server (server.exe) under “live/bin/”.And you can use “live/testProgs/testMp3Receiver.cpp” as the demo of the client. But a more sophisticated version of multimedia player, "VLC" can be used for a clear demonstration.
Download latest VLC @ http://www.videolan.org/vlc/download-windows.html or http://icourse.cuc.edu.cn/networkprogramming/assignments/RTSP/VLC/vlc-2.0.4-win32.zip and setup VLC.
2) Open the Open Media Dialog by click menu [Media] -> [Open Network Stream]
3) In the blank, type “rtsp://computing.cuc.edu.cn/Angel.mp3” for a test run.
注意:
你可能下载Live555在本课程的网页上,这里有两个版本,一个是在linux平台下使用,一个是在windows平台下使用。都含有代码和可执行文件。你可以使用其中的源码来分析RTSP的协议,并且启动可执行文件,用于测试你的客户端。
如果你选择Linux的版本,那么你可以在目录:“live/mediaServer/”下得到服务端可执行文件,即:live555MediaServer。如果你选择的windows的版本,你可以在目录:“live/bin/”下找到可执行文件server.exe。当然,你也利用一个小小的客户端的例子进行分析,代码位于:“live/testProgs/testMp3Receiver.cpp”。但是一个更高级的多媒体播放工具将能向您更清晰的展示功能,类似于一下步骤:
1、下载最新版本的VLC,并且安装。站点1,站点2
2、打开open media对话框,通过点击菜单栏的 [Media] -> [Open Network Stream]。
3、在空白处,输入:rtsp://computing.cuc.edu.cn/Angel.mp3,可测试播放曲目test。如下图所示。
Grading
Your project will be tested to make sure it works properly with the live555 media server.
Here is a rough breakdown of the grading:
- Basic function goes well 50%
- Advanced function goes well 20%
- Dealing with impolite requests, unexpected messages 10%
- Error handling, Style/Code structure, etc. 20%
- Extra credits:
- Ø Well defined project report +5 p
- Ø Elegant GUI interface +5 p
Note: 20% of your project grade depends on the how "well your code is written". These points include the following:
- Error handling (check every system call for an error!).
- Safe code (avoiding buffer overflow, etc).
- How well we can understand your code. There is no required format for your code; there is no requirement like "you must have one comment for every 2.35 lines of code". Feel free to provide whatever level of commenting you believe is appropriate to make sure that other competent programmers could easily understand and make changes to your code.
译:
你的项目将会被测试,能够很要的与Live555服务器通信,并且工作良好。
下面是一个大致的评分细则:
- 基本功能运行良好 50%
- 高级功能运行良好 20%
- 处理了不合理的请求和不可预料的情况 10%
- 错误处理,代码结构等 20%
- 额外加分:
- Ø 优秀的完成了作业报告 +5 p
- Ø 漂亮的UI界面 +5 p
注意:20%的分数将依据你的代码写的好不好,这主要是指:
- 错误处理,检查每一个有问题的系统调用。
- 代码安全,避免缓冲区溢出等。
- 代码是否让人容易理解,虽然没有明确要求你代码该如何写,比如没有如此的要求:你必须每2.35行代码就写一句注释。请自由发挥地添加注释,只要你相信这样做能够使得别的程序员能够很好地理解你的代码、利用你的代码、进一步修改你的代码。
(全文完)
自己的理解:
RTSP
- RealNetworks
- Netscape Communications
- 哥伦比亚大学
并且,继承于HTTP协议框架,拥有着HTTP的大部分特性。HTTP传送HTML,而RTSP传送的是多媒体数据(用于控制)。HTTP请求由客户机发出,服务器作出响应;使用RTSP时,客户机和服务器都可以发出请求,即RTSP可以是双向的。
- (Real Time Stream Protocol,实时流协议)
- 应用层协议(TCP/IP协议体系)
- 控制声音或影像的多媒体串流协议
- 音频与视频的受控、点播成为可能
- 遥控器: RTSP充当多媒体服务器的网络远程控制。
- 自行选择使用TCP或UDP来传送串流内容
关于RTSP的控制传输方式,可以分为三类。
- 用于多个请求/响应传输的持久传输连接:在RTSP连接期间,RTSP用户可打开或关闭多个对服务器的可传输连接以发出RTSP请求
- 用于单个请求/响应传输的一个连接
- 无连接模式:可使用无连接传输协议,如UDP。
- 请求消息
- 响应消息
- SP表示空格
- Request-URI用来指定资源的位置
- CRLF代表换行
- RTSP-Version= “RTSP” “/” 一位数字“.” 一位数字,如:RTSP/1.0
- Method如下解释
- Response=Status-Line
- *(general-header
- |response-header
- |entity-header )
- CRLF
- [ message-body ]
如:RTSP/1.0 200 OK
- SETUPrtsp://192.168.10.115:554/live.sdp/trackID=1RTSP/1.0
- CSeq: 3
- Transport: RTP/AVP/TCP;unicast;interleaved=0-1
- RTSP/1.0 200 OK
- CSeq: 3
- Date: Wed, 10 Nov 2010 11:28:10 GMT
- Session: 18119504;
- Transport: RTP/AVP/TCP;interleaved=0-1;unicast;mode=play
- rtsp = 使用可信的底层传输协议,例如TCP
- rtspu = 使用不可信的底层传输协议,例如UDP
- rtsps = 使用可信加密传输协议,例如TCP + TLS
- host = 服务器的IP地址
- port = 服务器的端口,该字段在忽略的下默认与服务器的554端口进行连接
- abs path = 所申请实时流的地址
有的服务器也支持下面的URL形式: rtsp://media.example.com:554/twister
实际上,RTSP是一个状态协议,一个会话从开始到结束,客户端和服务器都在维护一个状态,状态的定义基于由URL和会话ID唯一确定的对象。
在此过程中会话(session),作为RTSP交互的全过程非常重要的字段。比如,一个电影的观看过程。
会话(session)包括
- 由客户端建立连续媒体流传输机制(SETUP)
- 使用播放(PLAY)
- 录制(RECORD)开始传送流
- 用停止(TEARDOWN)关闭流。
- 客户端的状态转变是以收到请求的成功响应
- 发一个setup,请求成功,就变成了ready。
- (2XX)作为转变条件。如果收到3XX响应,状态立刻回到Init。而如果收到失败响应
- (4XX)则不改变状态。从服务器接受到REDIRECT和接受到3XX是等效的。
- 服务器的状态转变是以接受到请求并且发出响应为转移条件。
- 比如收到客户端的setup,那么服务端给客户端回一个成功的响应,那么服务端就转移状态
- 如果服务器处于Playing 或者 Recording状态,在一定的间隔内没有接收到客户端的健康信息(如RTCP报告或者RTSP命令),会回到Init状态并且结束这次RTSP会话。
RTP与RTCP
- Ver.(2 bits)是目前协议的版本号码,目前版号是 2。
- P(1 bit)是用于RTP 数据包(packet)退出点的预留空间,视数据包是否需要多余的填塞空间。
- X(1 bit)是否在使用延伸空间于数据包之中。.
- CC(4 bits)包含了 CSRC 数目用于修正标头(fixed header).
- M (1 bit) 是用于应用等级以及其原型(profile)的定义。如果不为零表示目前的数据有特别的程序解译
- PT(7 bits)是指payload的格式并决定将如何去由应用程序加以解译。
- SSRC 是同步化来源。
- Sequence Number - 每发送一个 RTP 数据包,序列号增加1。接收方可以依次检测数据包的丢失并恢复数据包序列。 但并不是说RTP保证所有帧能够按照序号送达。
- Timestamp - 反映 RTP 数据包中的第一个八位组的采样时间。采样时间必须通过时钟及时提供线性无变化增量获取,以支持同步和抖动计算。
- CSRC - 贡献源标识符。识别该数据包中的有效载荷的贡献源。 0~15个,每个CSRC 32位。此列表显示了对RTP数据包中的负载数据有贡献的数据源的SSRC。
因此,服务器可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。一般RTP和RTCP配合使用,能以有效的反馈和最小的开销使传输效率最佳化,故特别适合传送网上的实时数据。
实际上,RTP协议从上层接收流媒体信息码流,装配成RTP数据包发送给下层,下层协议提供RTP和RTCP的分流:如在UDP中,RTP使用一个偶数号端口,则相应的RTCP使用其后的奇数号端口。RTP数据包没有长度限制,它的最大包长只受下层协议的限制。
- SR:Sender report,现行的发送者的传输和接收的统计数据
- RR:Receiver report,由不是现行的发送者的接收统计数据
- SDES:一些源描述项,包括CNAME;
- BYE:指出参与结束;
- APP:应用程序特定功能
VLC
Section II Solution Method and Design
- socket联网类
- 与RTSP相关的类
- 播放(VLC)的类
类图
时序图
Section III Test Cases and Results Analysis
解释界面
输入错误的内容
输入正确的URL但不存在的歌曲
- rtsp://computing.cuc.edu.cn/Angel321.mp3。
正确的url
Section IV Conclusion
Section V References
Section VI Appendix
点击播放按钮
void CmediaPlayDlg::OnBnClickedOk()
{
if (RTSP_URL!="")//如果不为空,说明不是第一次,那么肯定是因为暂停键之后才想继续播放。
{
vlcPlayer_.Play();
CButton_play.EnableWindow(FALSE);
CButton_pause.EnableWindow(TRUE);
CButton_stop.EnableWindow(TRUE);
AddStringToLog("继续播放.....\r\n");
}else{
Cedit_Url.GetWindowText(RTSP_URL);
int flag=RTSP_URL.Find("rtsp://");
if(RTSP_URL==""||flag==-1)//判断是否为空
{
MessageBox("请输入正确的RTSP URL\r\n比如:\r\nrtsp://computing.cuc.edu.cn/Angel.mp3");
}else{
RTSP::RTSP_URL=RTSP_URL;
int n1=RTSP_URL.Find("//");
int n2=RTSP_URL.Find("/",n1+2);
CString hostName;
hostName = RTSP_URL.Mid(n1+2,n2-n1-2);//从输入中找到正确主机名
rtsp.MusicName=RTSP_URL.Mid(n2+1,RTSP_URL.GetLength());
AddStringToLog("hostName:"+hostName+"\r\n\r\n");
Socket socket;
hostIp=socket.getIpByhostName(hostName);
socket.InitSocket();
socket.BuildSocket(hostIp,"554");//554是默认的端口
socket.ConnectSocket();
AddStringToLog("Send Command:option.....\r\nGOT:\r\n");
string strTemp = rtsp.Options();
AddStringToLog(strTemp.c_str());
AddStringToLog("Send Command:Describe....\r\nGOT:\r\n");
strTemp = rtsp.Describe();
AddStringToLog(strTemp.c_str());
CString strTemp1;
strTemp1.Format("%s", strTemp.c_str());
int temp=strTemp1.Find("404");
if (temp!=-1)//如果等于-1表示没有找到,如果不等于-1表示找到,表示出错了
{
AddStringToLog("发生错误,请看最近一条信息。");
return;
}
AddStringToLog("\r\nSend Command:Setup....\r\nGOT:\r\n");
strTemp = rtsp.Setup();
AddStringToLog(strTemp.c_str());
//事先开启下载数据的线程
AfxBeginThread(getDataThread,(LPVOID)this,0,0,0,NULL);
AddStringToLog("\r\nSend Command:Play....\r\nGOT:\r\n");
strTemp = rtsp.Play();
AddStringToLog(strTemp.c_str());
//拿到数据之后开始播放
AfxBeginThread(playThread,(LPVOID)this,0,0,0,NULL);
//变一下button的颜色
CButton_play.EnableWindow(FALSE);
CButton_pause.EnableWindow(TRUE);
CButton_stop.EnableWindow(TRUE);
}
}
}
播放线程
UINT CmediaPlayDlg::playThread(LPVOID lpParam)
{
CmediaPlayDlg *Cdialog=(CmediaPlayDlg*)lpParam;
Cdialog->AddStringToLog("为了播放效果、缓冲3秒.....\r\n");
Sleep(3000);
string outputFilePathAndName="..\\"+rtsp.MusicName;
const char *ch = outputFilePathAndName.c_str();
CString file=ch;
vlcPlayer_.OpenMedia((LPCTSTR)file);
vlcPlayer_.Play();
Cdialog->AddStringToLog("开始播放.....\r\n");
return 1;
}
接受数据线程
UINT CmediaPlayDlg::getDataThread(LPVOID lpParam)
{
//拿到播放歌曲的名称
string outputFilePathAndName="..\\"+rtsp.MusicName;
const char *ch = outputFilePathAndName.c_str();
//每次下载的时候都把之前下载的删除掉,这是为了能够正常测试,但是实际上工程上不会怎么做。
if (PathFileExists(ch))
{
CFile TempFile;
TempFile.Remove(ch);
}
CmediaPlayDlg *Cdialog=(CmediaPlayDlg*)lpParam;
Cdialog->CListBox_log.AddString("开始下载......");
Cdialog->CListBox_musicName.AddString(rtsp.MusicName.c_str());
RTPSession rtpSession;
RTPUDPv4TransmissionParams transmissionParams;
RTPSessionParams rtpSessionParams;
//设置时间戳单元,根据传输的负载类型设置相应的时间戳单元
rtpSessionParams.SetOwnTimestampUnit(1.0/10.0);
rtpSessionParams.SetAcceptOwnPackets(true);
transmissionParams.SetPortbase(1554);
rtpSession.Create(rtpSessionParams,&transmissionParams);
//这一段是打开文件,往里面写东西,所以最好是只打开一次,不要反复的关闭文件
ofstream outputFile(ch,ios::out|ios::binary|ios::app);
if(!outputFile)
{
Cdialog->AddStringToLog("#ERROR:output File Fail...");
outputFile.clear();
return 0;
}
ifstream in(ch, ios::in|ios::binary);
//为了使服务端那边知道我们这边正常的接收了数据。
//我们将每次接收到一个数据包就记录一下,然后定期向服务器发一个option命令。
//告诉服务器客户端还活着
int packetCount=0;
do{
//开始接收数据包
rtpSession.BeginDataAccess();
if (rtpSession.GotoFirstSourceWithData())
{
do
{
RTPPacket *pack;
while ((pack = rtpSession.GetNextPacket()) != NULL)
{
packetCount++;
if(packetCount%2000==0){
rtsp.GetParameter(); //告诉服务器、客户端还活着
}
char *buf=(char *)pack->GetPayloadData(); //获取数据指针
outputFile.write(buf+4,pack->GetPayloadLength()-4);
rtpSession.DeletePacket(pack);
//下一段是为了读取下载的mp3文件,为了能够实时的显示在界面上,表示下载了多少
long l,m;
l = in.tellg();
in.seekg (0, ios::end);
m = in.tellg();
char fileSize[20];
char *pfileSize;
pfileSize=ltoa((m-1)/1024,fileSize,10);
CString str1;
Cdialog->CListBox_log.ResetContent();
str1.Format("已下载:%s KB",pfileSize);
Cdialog->CListBox_log.AddString(str1);
}
} while (rtpSession.GotoNextSourceWithData());
}
rtpSession.EndDataAccess();
rtpSession.Poll();
}while(1);
Cdialog->CListBox_log.ResetContent();
Cdialog->CListBox_log.AddString("下载完成......");
outputFile.close();
in.close();
rtpSession.BYEDestroy(RTPTime(10,0),0,0);
return 1;
}
RTSP类
#pragma once
#include "mediaPlay.h"
#include <string>
#include "Socket.h"
using namespace std;
#define BUF_SIZE 20000
class RTSP
{
public:
RTSP(void);
~RTSP(void);
static string RTSP_URL;
string sessionNo;
string content_base;
string MusicName;
string Options();
void GetParameter();
string Describe();
string Setup();
string Play();
string Pause();
string TearDown();
};
#include "StdAfx.h"
#include "RTSP.h"
RTSP::RTSP(void)
{
}
RTSP::~RTSP(void)
{
}
string RTSP::Options()
{
Socket socket;
char dataFromServer[BUF_SIZE];
string stringToSend;
string stringTemp("*ERROR:send Command:Options fail...");
stringToSend.clear();
stringToSend="OPTIONS "+RTSP_URL+" RTSP/1.0\r\n"+"CSeq: 1\r\n\r\n";
if(socket.Send(stringToSend.c_str(),stringToSend.length())==false)
{
return stringTemp;
}
if(socket.Recv(dataFromServer,BUF_SIZE)==false)
{
return stringTemp;
}
stringTemp=dataFromServer;
return stringTemp;
}
string RTSP::Describe()
{
Socket socket;
char dataFromServer[BUF_SIZE];
string stringToSend;
string stringTemp("*ERROR:send Command:Describe fail...");
stringToSend.clear();
stringToSend="DESCRIBE "+RTSP_URL+" RTSP/1.0\r\n"+"CSeq: 2\r\n"+"Accept: application/sdp\r\n\r\n";
if(socket.Send(stringToSend.c_str(),stringToSend.length())==false)
{
return stringTemp;
}
if(socket.Recv(dataFromServer,BUF_SIZE)==false)
{
return stringTemp;
}
stringTemp=dataFromServer;
CString strTemp=dataFromServer;
int n1 = strTemp.Find("Base:");
int n2 = strTemp.Find("Content-Type",n1+5);
content_base = strTemp.Mid(n1+5,n2-n1-7);
return stringTemp;
}
string RTSP::Setup()
{
Socket socket;
char dataFromServer[BUF_SIZE];
string stringToSend;
string stringTemp("*ERROR:send Command:Setup fail...");
stringToSend.clear();
stringToSend="SETUP "+content_base+"track1"+" RTSP/1.0\r\n"+"CSeq: 3\r\n"+"Transport: RTP/AVP;unicast;client_port=1554-1555\r\n\r\n";
if(socket.Send(stringToSend.c_str(),stringToSend.length())==false)
{
return stringTemp;
}
if(socket.Recv(dataFromServer,BUF_SIZE)==false)
{
return stringTemp;
}
CString strTemp=dataFromServer;
int n1 = strTemp.Find("Session:");
int n2 = strTemp.Find("\r",n1+8);
sessionNo = strTemp.Mid(n1+8,n2-n1-8);
stringTemp=dataFromServer;
return stringTemp;
}
string RTSP::Play()
{
Socket socket;
char dataFromServer[BUF_SIZE];
string stringToSend;
string stringTemp("*ERROR:send Command:Setup fail...");
stringToSend.clear();
stringToSend="PLAY "+content_base+" RTSP/1.0\r\n"+"CSeq: 4\r\n"+"Session: "+sessionNo+"\r\nRange: npt=0.000-\r\n\r\n";
if(socket.Send(stringToSend.c_str(),stringToSend.length())==false)
{
return stringTemp;
}
if(socket.Recv(dataFromServer,BUF_SIZE)==false)
{
return stringTemp;
}
stringTemp=dataFromServer;
return stringTemp;
}
void RTSP::GetParameter()
{
Socket socket;
string stringToSend;
stringToSend.clear();
stringToSend="GET_PARAMETER "+content_base+" RTSP/1.0\r\n"+"CSeq: 5\r\n"+"Session: "+sessionNo+"\r\n\r\n";
socket.Send(stringToSend.c_str(),stringToSend.length());
}
string RTSP::Pause()
{
Socket socket;
char dataFromServer[BUF_SIZE];
string stringToSend;
string stringTemp("*ERROR:send Command:Pause fail...");
stringToSend.clear();
stringToSend="PAUSE "+content_base+" RTSP/1.0\r\n"+"CSeq: 6\r\n"+"Session: "+sessionNo+"\r\n\r\n";
if(socket.Send(stringToSend.c_str(),stringToSend.length())==false)
{
return stringTemp;
}
if(socket.Recv(dataFromServer,BUF_SIZE)==false)
{
return stringTemp;
}
stringTemp=dataFromServer;
return stringTemp;
}
string RTSP::TearDown()
{
Socket socket;
char dataFromServer[BUF_SIZE];
string stringToSend;
string stringTemp("*ERROR:send Command:TearDown fail...");
stringToSend.clear();
stringToSend="TEARDOWN "+content_base+" RTSP/1.0\r\n"+"CSeq: 7\r\n"+"Session: "+sessionNo+"\r\n\r\n";
if(socket.Send(stringToSend.c_str(),stringToSend.length())==false)
{
return stringTemp;
}
if(socket.Recv(dataFromServer,BUF_SIZE)==false)
{
return stringTemp;
}
stringTemp=dataFromServer;
return stringTemp;
}
socket类
#pragma once
#include <winsock2.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
class Socket
{
public:
Socket(void);
~Socket(void);
bool InitSocket();
bool BuildSocket(u_long ip,CString port);
bool ConnectSocket();
u_long getIpByhostName(CString hostName);
bool Send(const char *c,UINT count);
int Recv(char *c,UINT count);
bool Close();
static Socket SocketToConnect;
static SOCKET socketNo;
SOCKADDR_IN socketAddr;
private:
};
#include "StdAfx.h"
#include "Socket.h"
#include <iostream>
#include <string>
#include <ws2tcpip.h>
#include "mediaPlay.h"
#include "mediaPlayDlg.h"
using namespace std;
Socket::Socket(void)
{
}
Socket::~Socket(void)
{
}
SOCKET Socket::socketNo;
u_long Socket::getIpByhostName(CString hostName)
{
WORD wVersionRequested; //获取服务器Ip地址
WSADATA wsaData;
PHOSTENT hostinfo;
wVersionRequested=MAKEWORD(2,2);
WSAStartup(wVersionRequested,&wsaData);
hostinfo=gethostbyname(hostName);
return inet_addr(inet_ntoa(*(struct in_addr*)*hostinfo->h_addr_list));
}
bool Socket::InitSocket()
{
WSADATA wsaData;
int iResult;
if((iResult=WSAStartup(MAKEWORD(2,2),&wsaData))!=NO_ERROR)
{
return false;
}
socketNo=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(socketNo==INVALID_SOCKET)
{
return false;
}
return 1;
}
bool Socket::BuildSocket(u_long ip,CString port)
{
socketAddr.sin_family=AF_INET;
socketAddr.sin_port=htons((u_short)atoi(port));
socketAddr.sin_addr.s_addr=ip;
return 1;
}
bool Socket::ConnectSocket()
{
int iResult=0;
iResult=connect(socketNo, (SOCKADDR*)&socketAddr, sizeof(socketAddr));
if (iResult==SOCKET_ERROR)
{
return false;
}
return true;
}
bool Socket::Close()
{
closesocket(socketNo);
return true;
}
bool Socket::Send(const char *c,UINT length)
{
if(send(socketNo,c,length,0)==SOCKET_ERROR)
{
return false;
}
return true;
}
int Socket::Recv(char *c,UINT length)
{
memset(c,'\0',length);
return recv(socketNo,c,length,0);
}
林老师最后一次大作业:Project 10 – RTSP Windows Client: RFC 2326相关推荐
- 跟着王家林老师实现自己的大数据梦想
Hadoop的出现引起大数据的浪潮来临,但是,这仅仅是大数据时代的开始,随着大数据时代的到来,大数据应用慢慢地深入我们的生活的每一个角落,我们对大数据充满无比好奇,却对他们了解的很少,生活在大数据时代 ...
- HTML+CSS大作业——神偷奶爸(10页) HT简单个人网页设计作业 静态动漫主题网页作业 DW个人网站模板下载 大学生简单个人网页作品代码
HTML5期末大作业 文章目录 HTML5期末大作业 一.作品展示 二.文件目录 三.代码实现 一.作品展示 二.文件目录 三.代码实现 <!DOCTYPE html PUBLIC " ...
- 互动媒体大作业——绘画系统
绘画系统实验心得 这学期接触了"互动媒体技术"这门课,感触颇深.因为在第四维(根据爱因斯坦相对论),在时间上的重复,在传统静态绘画中是不可能实现的,但用编程就可以做到.这样一来,通 ...
- 大学期间所有课设及大作业源代码
程序员练成记 出处:牟尼的专栏 http://blog.csdn.net/u012027907 记得刚上大一时,那时对这个专业(软件工程)很陌生,甚至对电脑都很陌生,有时还有点儿害怕,因为我在进入大学 ...
- HIT CS:APP 计算机系统大作业 《程序人生-Hello’s P2P》
HIT CS:APP 计算机系统大作业 程序人生-Hello's P2P Hello的自白 我是Hello,我是每一个程序猿¤的初恋(羞羞--) l却在短短几分钟后惨遭每个菜鸟的无情抛弃(呜呜--), ...
- 基于JavaSwing开发蜘蛛纸牌游戏 课程设计 大作业源码
基于JavaSwing开发蜘蛛纸牌游戏: (大作业) 开发环境: Windows操作系统 开发工具: Eclipse+Jdk 运行效果图: 基于JavaSwing开发蜘蛛纸牌游戏: (大作业) ...
- 基于JavaSwing开发学生信息管理系统(SQLServer数据库版本) 毕业设计 课程设计 大作业
基于JavaSwing开发学生信息管理系统(SQLServer数据库版本): (大作业) 开发环境: Windows操作系统 开发工具: MyEclipse+Jdk+SQLServer数据库 运行 ...
- 基于JavaSwing开发模拟电梯系统+分析报告 课程设计 大作业源码
基于JavaSwing开发模拟电梯系统+分析报告: (大作业) 开发环境: Windows操作系统 开发工具: MyEclipse+Jdk 运行效果图: 基于JavaSwing开发模拟电梯系统+分 ...
- 基于JavaSwing开发书店管理系统+论文 毕业设计 课程设计 大作业
基于JavaSwing开发书店管理系统+论文: (大作业) 开发环境: Windows操作系统 开发工具: MyEclipse+Jdk1.6+Mysql数据库 运行效果图: 基于JavaSwi ...
最新文章
- 使用SpringMVC创建支持向下兼容的版本化的API接口
- 颠倒整数的C语言程序,帮忙解决c语言题目1.编写一个程序,用户输入一个小于5位的正整数,把它的各位数字前后颠倒一下,并输出颠倒后的结果。2.编...
- MPC class get last modified - how to implement
- SQL server中DateTime类型字段如何赋值零?
- MyEclipes+JSP+SSH+MySQL实现一个文章发布系统
- (转)Struts2访问Servlet的API及......
- java垃圾收集器zgc_java虚拟机ZGC垃圾收集器的实现方法
- DefenseCode ThunderScan 静态代码审计工具
- 金三银四的面试黄金季节,Android面试题来了!
- 使用Eclipse-Maven-git做Java开发(9)--eclipse新建maven结构工程
- 调用百度API实现人像动漫化(C++)
- 困难之下见证良心公司!!
- phpize的作用(资料整理)
- Ansible tower 3.7.0-4自动化运维管理安装方法
- winrar的破解激活
- word中公式和文字不在一条水平线上
- php laravel mix,引入 Laravel Mix 管理前端资源
- 一个人被提拔,不仅仅是能力,而是信任
- 如何在vue中设置全局方法
- 软件测试-web自动化测试教程
热门文章
- 蓝牙锁定计算机,Windows10创意者如何启用动态蓝牙锁功能?
- 喷涂机器人主要组成部分
- 浅谈彩色图像、灰度图像、二值图像和索引图像区别
- kindeditor批量上传设置_ftp如何使用,ftp如何使用,3步掌握安装及使用方法 - 批量远程桌面管理服务器、vps教程...
- java做一个mud_我应该如何处理Java MUD中的持久性? Optimis...
- 货拉拉 Android H5离线包原理与实践
- 解决SecureCRT超时连接自动断开
- python字符串最后一次的索引_关于python:如何每3个索引切一个字符串?
- biden1挖矿病毒处理经过
- koa--批量上传图片