5. Kurento教程
这部分包含了如何使用Kurento框架的教程,以创建不同类型WebRTC和多媒体应用。教程包含下面三个方面:
? Java:
These show applications where clients interact with an application server based on Java EE technology.
The application server hold the logic orchestrating the communication
among the clients and controlling Kurento Server capabilities for them.
? Browser JavaScript:
These show applications executing at the browser and communicating directly with the Kurento Media Server.
In these tutorial, all the application logic is hold by the browser. Hence, no application
server is necessary. For these reasons, these applications need to be simple.
? Node.js:
These show applications where clients interact with an application server based on Node.js technology.
下面这个例子是使用Kurento创建的一个最简单的WebRTC应用。
它实现一个WebRTC回看(一个WebRTC媒体流从客户端发送到Kurento,然后再返回到客户端)。
5.1 Java 教程1 - Hello world
这个网页应用程序是为了给Java开发者介绍Kurento开发规则的。它包含一个基于WebRTC视频回看功能。5.1.1 运行示例程序
在运行本示例前,需要先安装 Kurento Media Server,可见前面的安装指南。另外,你还需要先在你的系统中安装 JDK (at least version 7), Maven, Git, 以及Bower。
Bower的可以使用npm(Node.js的包管理器)来安装。在Ubuntu机器上,它的安装命令如下:
# sudo apt-get install curl
# curl -sL https://deb.nodesource.com/setup | sudo bash -
# sudo apt-get install -y nodejs
# sudo npm install -g bower
为了加载这个应用程序,你需要先从GibHub项目上克隆,安装并运行主类,命令如下:
# git clone https://github.com/Kurento/kurento-tutorial-java.git
# cd kurento-tutorial-java/kurento-hello-world
# mvn clean compile exec:java
程序启动后,就可用兼容WebRTC的浏览器(Chrom, Firefox),输入URL http://localhost:8080/ 来测试。
5.1.2 运行时的出错
运行命令mvn clean compile exec:java 时的出错处理1. 错误提示:
[INFO] --------------------------------
[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal>or<plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. …
…
[ERROR]
[ERROR] For more information about the errors and possible solution, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/NoGoalSpecifiedException
使用下面的命令追踪错误的更详细信息
mvn –X
解决办法:
详解解释见它的提示网页:
https://cwiki.apache.org/confluence/display/MAVEN/NoGoalSpecifiedException
在pom.xml文件中添加如下标签:
<project>
...
<build>
<defaultGoal>compile</defaultGoal>
...
</build>
...
</project>
2. 错误提示
bower jquery#>= 1.9.1 cached git://github.com/jquery/jquery.git#2.1.4
bower jquery#>= 1.9.1 validate 2.1.4 against git://github.com/jquery/jquery.git#>= 1.9.1
bower EACCES EACCES, mkdir '/opt/kurento/kurento-tutorial-java/kurento-one2many-call/src/main/resources/static/bower_components'
Stack trace:
Error: EACCES, mkdir '/opt/kurento/kurento-tutorial-java/kurento-one2many-call/src/main/resources/static/bower_components'
at Error (native)
Console trace:
Error
at StandardRenderer.error (/usr/local/lib/node_modules/bower/lib/renderers/StandardRenderer.js:82:37)
at Logger.<anonymous> (/usr/local/lib/node_modules/bower/bin/bower:110:22)
at Logger.emit (events.js:109:17)
at Logger.emit (/usr/local/lib/node_modules/bower/node_modules/bower-logger/lib/Logger.js:29:39)
at /usr/local/lib/node_modules/bower/lib/commands/index.js:45:20
at _rejected (/usr/local/lib/node_modules/bower/node_modules/q/q.js:844:24)
at /usr/local/lib/node_modules/bower/node_modules/q/q.js:870:30
at Promise.when (/usr/local/lib/node_modules/bower/node_modules/q/q.js:1122:31)
at Promise.promise.promiseDispatch (/usr/local/lib/node_modules/bower/node_modules/q/q.js:788:41)
at /usr/local/lib/node_modules/bower/node_modules/q/q.js:604:44
System info:
Bower version: 1.4.1
Node version: 0.13.0-pre
OS: Linux 3.16.0-23-generic x64
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.512s
[INFO] Finished at: Wed Jul 08 18:16:09 CST 2015
[INFO] Final Memory: 17M/457M
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.3.2:exec (default) on project kurento-hello-world: Command execution failed. Process exited with an error: 1 (Exit value: 1) -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.3.2:exec (default) on project kurento-hello-world: Command execution failed.
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:217)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoExecutionException: Command execution failed.
at org.codehaus.mojo.exec.ExecMojo.execute(ExecMojo.java:303)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
... 19 more
Caused by: org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
at org.apache.commons.exec.DefaultExecutor.executeInternal(DefaultExecutor.java:402)
at org.apache.commons.exec.DefaultExecutor.execute(DefaultExecutor.java:164)
at org.codehaus.mojo.exec.ExecMojo.executeCommandLine(ExecMojo.java:746)
at org.codehaus.mojo.exec.ExecMojo.execute(ExecMojo.java:292)
... 21 more
[ERROR]
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
解决方案:
是因为这句话:
Error: EACCES, mkdir '/opt/kurento/kurento-tutorial-java/kurento-one2many-call/src/main/resources/static/bower_components'
它在编译过程中会要创建和删除目录及文件,因为文件夹的权限不对,所以导致的这个错误。
使用命令更改文件夹的权限:
$ sudo chown username –R kurento-hello-world
$ sudo chmod +777 kurento-hello-world
5.1.3 示例程序的分析
Kurento提供给开发者一个 Kurento Java客户端来控制 Kurento Media Server。这个客户端库可以被任何Java应用程序使用,如Server Side Web, Desktop, Android等。
它也兼容所有像JavaEE,Spring, Play, Vert.x, 和JavaFx框架。
这个Hello world的DEMO是Kurento最简单的Web应用,下面的图片是这个DEMO运行时的截图。
Figure 6.1: Kurento Hello World Screenshot: WebRTC in loopback
这个应用程序的接口(一个HTML页面)是由两个HTML5标签组成的:
一个用来显示本地视频流(由摄像头捕捉到的);
另一个用来显示由媒体服务器发送给客户端的远端视频流。
这个应用程序的逻辑很简单:
将本地流发送到Kurento Media Server,它不对流做任何修改直接返回给客户端。
为了实现这个逻辑,我们需要创建一个由单个Media Element组成的Media Pipeline。
Media Element可以是WebRtcEndpoint, 它具有WebRTC媒体流的全双向数据交换能力。
这个媒体元素和它自己连接,因此,它的在浏览器接收到的媒体是从浏览器发送出去的。
这个媒体管道的示例图如下:
这个页面应用程序同样的客户-服务端架构。
在客户端,它的逻辑是由JavaScript实现的。
在服务端,我们使用了Java应用程序服务器来消费Kurento Java Client API对Kurento Media Server能力的控制。
总之,这个示例的高级架构是三层。
为了实现实体间的通信使用了下面的技术:
? REST: 用于在JavaScript客户端和Java应用程序的服务端进行通信。
? WebSocket: 用于在 Kurento Java客户端和 Kurento Media Server间进行通信。
这个通信是由Kurento协议实现的。更多细节见协议文档
Figure 6.2: Kurento Hello World Media Pipeline in context
下面的时序图显示了三个应用程序接口层的交互时序:
i) JavaScript logic;
ii) Application server logic (which uses the Kurento Java Client);
iii) Kurento Media Server
Note: 客户和服务端的通信可以不需要REST,为了简单化,在这个DEMO中使用了REST。
在后面的例子中,客户和服务端使用了更复杂的信令通信机制,WebSockets。
The following sections analyze in deep the server (Java) and client-side (JavaScript)
code of this application. The complete source code can be found in GitHub.
下面的章节深度分析了这个DEMO的服务端(Java)和客户端(JavaScript)的代码。完整的源码可以从GitHub上下载。
5.1.4 应用程序服务端逻辑
这个DEMO在服务端使用了Java的Spring Boot框架,这个技术可以被嵌入到Tomcat网页服务器中使用,从而简化了开发进程。
Note: You can use whatever Java server side technology you prefer to build web applications with Kurento.
For example, a pure Java EE application, SIP Servlets, Play, Vert.x, etc.
Here we chose Spring Boot for convenience.
在下面的示图中,可以看到服务端代码的类图。
这个DEMO的主类是HelloWorldApp, 如代码中所见,KurentoClient是Spring Bean的实例。
这个组件用来创建Kurento Media Pipelines, 它用来给应用程序添加媒体的能力。
在这个实例中,我们可以看到需要对客户端库指定Kurento Media Server的位置。
在这个示例中,我们假定它位于本机的8888端口。如果你要重构这个DEMO,你需要自已指定你的Kurento Media Server 实例的位置。
当Kurento Client被实例化后,就可以准备和Kurento Media Server通信并控制它的媒体能力。
Figure 6.3: Complete sequence diagram of Kurento Hello World (WebRTC in loopbak) demo
Figure 6.4: Server-side class diagram of the HelloWorld app
@ComponentScan
@EnableAutoConfiguration
public class HelloWorldApp {
@Bean
public KurentoClient kurentoClient() {
return KurentoClient.create("ws://localhost:8888/kurento");
}
public static void main(String[] args) throws Exception {
new SpringApplication(HelloWorldApp.class).run(args);
}
}
如前所述,我们使用了REST进行客户端与Java应用程序服务端的通信。
特别的,我们在服务端使用Spring声明 @RestController来实现REST服务。
下面来看HelloWorld-Controller类:
@RestController
public class HelloWorldController {
@Autowired
private KurentoClient kurento;
@RequestMapping(value = "/helloworld", method = RequestMethod.POST)
private String processRequest(@RequestBody String sdpOffer)
throws IOException {
// Media Logic
MediaPipeline pipeline = kurento.createMediaPipeline();
WebRtcEndpoint webRtcEndpoint = new WebRtcEndpoint.Builder(pipeline).build();
webRtcEndpoint.connect(webRtcEndpoint);
// SDP negotiation (offer and answer)
String responseSdp = webRtcEndpoint.processOffer(sdpOffer);
return responseSdp;
}
}
应用程序逻辑是在processRequest方法中实现的。
POST请求将路径 /helloworld 发送出去,它主要执行两个部分:
? 配置媒体处理逻辑:
在应用程序中,这个部分配置了Kurento如何来处理媒体。换句话说,是在这里创建了媒体管道。
为了这个目的,对象KurentoClient用来创建一个MediaPipeline对象,通过它,我们所需要的媒体元素被创建并连接。
在这个例子中,我们只需要初始化一个WebRtcEndpoint来接收WebRTC流,然后再把它发回给客户端。
? WebRTC SDP 协商:
在WebRTC中,SDP (Session Description protocol) 用来在App间进行媒体数据交换的协商。
这种协商的发生是基于SDP提交和回答的交换机制。
在这个例子中,我们假设SDP的提交和回答包含了所有WebRTC ICE候选者。
这个协商是在 processRequest 方法的第二部分实现的,在浏览器客户端使用了SDP提交,
然后由WebRtcEndPoint生成一个SDP回答并返回。
5.1.5 客户端逻辑
接着来看客户端的应用程序,它是一个单页面应用程序框架(SPA)。为了呼叫前面创建REST服务,我们使用了JavaScript库jQuery。
另外,我们使用了Kurento JavaScript 应用库,叫做Kurento-utils.js,来简化浏览器中WebRTC的管理。
这个库依赖于adapter.js,它是一个JavaScript WebRTC应用,由Google管理,它抽象了浏览器的差异。
最后这个应用程序也需要jquery.js。
These libraries are linked in the index.html web page, and are used in the index.js.
In the start function we can see how jQuery is used to send a POST request to the path /helloworld,
where the application server REST service is listening.
The function WebRtcPeer.startSendRecv abstracts the WebRTC internal details (i.e. PeerConnection and getUserStream)
and makes possible to start a full-duplexWebRTC communication, using the HTML video tag with id videoInput to
show the video camera (local stream) and the video tag videoOutput to show the remote stream provided by the Kurento Media Server.
var webRtcPeer;
function start() {
console.log("Starting video call ...");
showSpinner(videoInput, videoOutput);
webRtcPeer =
kurentoUtils.WebRtcPeer.startSendRecv(videoInput, videoOutput, onOffer, onError);
}
function onOffer(sdpOffer) {
console.info('Invoking SDP offer callback function ' + location.host);
$.ajax({
url : location.protocol + '/helloworld',
type : 'POST',
dataType : 'text',
contentType : 'application/sdp',
data : sdpOffer,
success : function(sdpAnswer) {
console.log("Received sdpAnswer from server. Processing ...");
webRtcPeer.processSdpAnswer(sdpAnswer);
},
error : function(jqXHR, textStatus, error) {
onError(error);
}
});
}
function onError(error) {
console.error(error);
}
5.1.6 依赖库
This Java Spring application is implemented using Maven.The relevant part of the pom.xml is where Kurento dependencies are declared.
As the following snippet shows, we need two dependencies: the Kurento Client Java dependency
(kurento-client) and the JavaScript Kurento utility library (kurento-utils) for the client-side:
<dependencies>
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-client</artifactId>
<version>[5.0.0,6.0.0)</version>
</dependency>
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-utils-js</artifactId>
<version>[5.0.0,6.0.0)</version>
</dependency>
</dependencies>
Kurento framework uses Semantic Versioning for releases.
Notice that range [5.0.0,6.0.0) downloads the latest version of Kurento artefacts
from Maven Central in version 5 (i.e. 5.x.x). Major versions are released when incompatible changes are made.
Note: We are in active development. You can find the latest version of Kurento Java Client at Maven Central.
Kurento Java Client has a minimum requirement of Java 7.
Hence, you need to include the following in the properties section:
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.7</maven.compiler.source>