锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 流媒体开发培训 / C#流媒体开发入门培训
服务方向
软件开发培训
Java 安卓移动开发
Java Web开发
HTML5培训
iOS培训
网站前端开发
VC++
C#软件
C语言(Linux)
ASP.NET网站开发(C#)
C#软件+ASP.NET网站
SOCKET网络通信开发
COMOLE和ActiveX开发
C++(Linux)
汇编和破解
驱动开发
SkinMagicVC++换肤
MicroStation二次开发
计算机英语翻译
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft
C#流媒体开发入门培训

C#流媒体开发入门培训


本网页内容出自锐英源原创,任何内容的侵权使用,必定追究。


引子

C#流媒体开发入门培训是锐英源流媒体开发培训的子系列,本系统能学到流媒体开发的核心特性,对“流”的掌握能做到有效加强,主要覆盖范围有:音视频数据采集、数据上传、http服务器接收、服务器多播支持、客户端下载和客户端播放等等,面向个人、企业和学校进行培训和技术服务。锐英源能够踏踏实实把开源和英语进行完美结合,奉献给大家的都是精华集萃!下面内容是培训的大纲和一些部分的文字内容。

培训特点

结合快速开发,快速形成战斗力。C#是快速开发语言,本系列在C#环境下进行指导培训。

结合项目应用,直播服务器架构是关键架构。

结合疑难处理,vlc是开源平台,功能不全面,但是锐英源研究了一套方法实现了vlc下的音视频同步,使采集和播放能同步。


培训大纲

1.nvlc

nVLC演示

DLL接口技术

C#类图,内存处理和日志机制

代码使用

播放

串流

内存渲染,高级内存渲染,PTS和DTS

2.http直播服务器

采集接口

上传接口

服务器接收接口

服务器环形缓冲多播接口

3.播放客户端

nVLC下载

nVLC播放


HTTP直播服务器开发相关培训内容

1基础

1.1通信和HTTP协议介绍

通信:通过网卡或其它媒介交换数据。
HTTP协议:下载网页文件的重要协议。

1.2SOCKET和SOCKET选项

操作系统里管理数据通信用SOCKET,可以理解为通信句柄。其功能就象数据的转手者,在操作系统和应用程序之间转手。 怎么转手,可以用选项来控制。

listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress,  true);

通信地址的使用可以重用。频繁地使用某个转手源是允许的。还有其它选项。

1.3Hashtable

数据结构,类似C++里的map。键值对。

1.4流

基于一个通道,把数据按序列读出或写入。流有数据类型管理能力。在流媒体里,数据类型泛化为各种协议处理。

        inputStream = new BufferedStream(socket.GetStream());
                  // we  probably shouldn't be using a streamwriter for all output from handlers either
        outputStream = new StreamWriter(new BufferedStream(socket.GetStream()));

这些是用socket的通道来建立输入流和输出流,输入和输出是基于程序角度,不是基于socket通道角度。通道给程序数据,就是输入,反之则是输出。

1.5BufferedStream

给另一流上的读写操作添加一个缓冲层。以提高某些 I/O 操作的性能。先让这个类通过自己的缓冲管理数据,再提交给另一个流。

合理地使用缓冲,肯定会增强性能。    

1.6StreamWriter

写入流。
可以用WriteLine,而BufferedStream没有这个成员方法,所以要用这个类。而WriteLine会自动添加回车换行。

1.5线程

概念不多讲。C#里Thread可以实现线程处理,ThreadStart里指定线程方法。线程方法里带循环,则会一直运行,如果不是循环形式,则只运行一次就退出线程。线程方法退出时,线程就不存在了。

1.6正文

Using the Code
First let's review how to use the class, and then we'll dig into some of the details of how it operates. We begin by subclassing HttpServer and providing implementations for the two abstract methods handleGETRequest and handlePOSTRequest...从HttpServer派生类讲起,此类要实现2个虚方法handleGETRequest 和 handlePOSTRequest

        public class MyHttpServer :  HttpServer {
public MyHttpServer(int port)
: base(port) {
}
public override void handleGETRequest(HttpProcessor p) {
Console.WriteLine("request: {0}", p.http_url);
p.writeSuccess();
p.outputStream.WriteLine("<html><body><h1>test server</h1>");
p.outputStream.WriteLine("Current Time: " + DateTime.Now.ToString());
p.outputStream.WriteLine("url : {0}", p.http_url);

p.outputStream.WriteLine("<form method=post action=/form>");
p.outputStream.WriteLine("<input type=text name=foo value=foovalue>");
p.outputStream.WriteLine("<input type=submit name=bar value=barvalue>");
p.outputStream.WriteLine("</form>");
}

public override void handlePOSTRequest(HttpProcessor p, StreamReader inputData) {
Console.WriteLine("POST request: {0}", p.http_url);
string data = inputData.ReadToEnd();

p.outputStream.WriteLine("<html><body><h1>test server</h1>");
p.outputStream.WriteLine("<a href=/test>return</a><p>");
p.outputStream.WriteLine("postbody: <pre>{0}</pre>", data);
}
}

Once a simple request processor is provided, one must instantiate the server on a port, and start a thread for the main server listener.一旦一个简单的请求处理有了,必须基于一个端口来初始化服务器且开启一个线程做主服务器监听。

        HttpServer httpServer = new  MyHttpServer(8080);
Thread thread = new Thread(new ThreadStart(httpServer.listen));
thread.Start();
If you compile and run the sample project, you should be able to point a web-browser of choice at http://localhost:8080 to see the above simple HTML pages rendered. Let's take a brief look at what's going on under the hood.执行示例程序后,可以用http://localhost:8080这样的URL来打开简单的HTML页面。
This simple webserver is broken into two components. The HttpServer class opens a TcpListener on the incoming port, and sits in a loop handling incoming TCP connect requests using AcceptTcpClient(). This is the first step of handling an incoming TCP connection. The incoming request arrived on our "well known port", and this accept process creates a fresh port-pair for server to communicate with this client on. That fresh port-pair is our TcpClient session. This keeps our main accept port free to accept new connections. As you can see in the code below, each time the listener returns a new TcpClient, HttpServer creates a new HttpProcessor and starts a new thread for it to operate in. This class also contains the abstract methods our subclass must implement in order to produce a response.在监听端口上使用TcpListener,在循环里处理来临的TCP连接请求,使用AcceptTcpClient()来处理。AcceptTcpClient执行后,会创建新的配对端口使客户端和服务器能够通信。新配对端口在TcpClient事务里。每当监听者返回一个新的TcpClient,HttpServer创建一个新的HttpProcessor,且创建一个新的线程来操作。虚函数要负责产生响应数据。

        public abstract class HttpServer {
           protected int port;
TcpListener listener;
bool is_active = true;

public HttpServer(int port) {
this.port = port;
}

public void listen() {
listener = new TcpListener(port);
listener.Start();
while (is_active) {               
TcpClient s = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(s, this);
Thread thread = new Thread(new ThreadStart(processor.process));
thread.Start();
Thread.Sleep(1);
}
}

public abstract void handleGETRequest(HttpProcessor p);
public abstract void handlePOSTRequest(HttpProcessor p, StreamReader inputData);
}

At this point, the new client-server TCP connection is handed off to the HttpProcessor in its own thread. The HttpProcessor's job is to properly parse the HTTP headers, and hand control to the proper abstract method handler implementation. Let's look at just a few small parts of the HTTP header processing. The first line of an HTTP Request resembles the following: HttpProcessor的任务是合适地解析HTTP包头,传递控制到合适的abstract方法-实现处理的方法。包头解析。HTTP请求的第一行如下组织:
GET /myurl HTTP/1.0
After setting up the input and output stream in process(), our HttpProcessor calls parseRequest(), where the above HTTP request line is received and parsed.在process()方法里配置好输入和输出流后,HttpProcessor调用parseRequest(),它会把上面说的HTTP请求行接收和解析。

        public void parseRequest() {
String request = inputStream.ReadLine();
string[] tokens = request.Split(' ');
if (tokens.Length != 3) {
throw new Exception("invalid http request line");
}
http_method = tokens[0].ToUpper();
http_url = tokens[1];
http_protocol_versionstring = tokens[2];     Console.WriteLine("starting: " + request);
}

The HTTP request line is always three parts, so we simply use a string.Split() call to separate it into three pieces. The next step is to receive and parse the HTTP headers from the client. Each header-line includes a type of the form KEY:Value. An empty line signifies the end of the HTTP headers. Our code to readHeaders is the following:HTTP请求行总是由三部分组成,所以我们简单地用string.Split()方法来分离三个部分。下一步是接收和解析HTTP包头。每个包头行包含形式为KEY:Value。空行指示包头结束。

        public void readHeaders() {
Console.WriteLine("readHeaders()");
String line;
while ((line = inputStream.ReadLine()) != null) {
if (line.Equals("")) {
Console.WriteLine("got headers");
return;
}

int separator = line.IndexOf(':');
if (separator == -1) {
throw new Exception("invalid http header line: " + line);
}
String name = line.Substring(0, separator);
int pos = separator + 1;
while ((pos < line.Length) && (line[pos] == ' ')) {
pos++; // strip any spaces
}

string value = line.Substring(pos, line.Length - pos);
Console.WriteLine("header: {0}:{1}",name,value);
httpHeaders[name] = value;
}
}
For each line, we look for the colon separator, grabbing the string before as a name, and the string after as a value. When we reach an empty header-line, we return because we have received all headers.通过分号分离每行,分号前是名称,分号后是值。当接收空行时,表示结束。
At this point, we know enough to handle our simple GET or POST, so we dispatch to the proper handler. In the case of a post, there is some trickiness to deal with in accepting the post data. One of the request headers includes the content-length of the post data. While we wish to let our subclass's handlePOSTRequest actually deal with the post data, we need to only allow them to request content-length bytes off the stream, otherwise they will be stuck blocking on the input stream waiting for data which will never arrive. 在处理post时,有些技巧来处理接收post数据。请求包头中的一个会包含post数据的content-length。因为我们想让子类的handlePOSTRequest 实际处理post数据,我们只需要允许它们请求content-length字节数数据,否则会卡在等待永远到达不了的数据输入上。In this simple server, we handle this situation with the dirty but effective strategy of reading all the post data into a MemoryStream before sending this data to the POST handler. This is not ideal for a number of reasons. First, the post data may be large. In fact it may be a file upload, in which case buffering it into memory may not be efficient or even possible. 我们用不太正规的方法高效处理了这个问题,把所有post数据读取到MemoryStream里。不太正规是指,首先,post数据可能很大,全缓冲到内存里效率差或不可行。Ideally, we would create some type of stream-imitator that could be setup to limit itself to content-length bytes, but otherwise act as a normal stream. This would allow the POST handler to pull data directly off the stream without the overhead of buffering in memory. However, this is also much more code. In many embedded HTTP servers, post requests are not necessary at all, so we avoid this situation by simply limiting POST input data to no more than 10MB.理想状态下,用流模拟器来设置限额为content-length,其它行为和正常流一样。这要用大量代码。所以这里只是简单限制POST输入数据为10MB.
Another simplification of this simple server is the content-type of the return data. In the HTTP protocol, the server always sends the browser the MIME-Type of the data which it should be expecting. In writeSuccess(), you can see that this server always indicates a content-type of text/html. If you wish to return other content types, you will need to extend this method to allow your handler to supply a content type response before it sends data to the client.内容类型固定为text/html,在writeSuccess方法里。

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内