[+] 终端
This commit is contained in:
88
README.md
88
README.md
@@ -1,14 +1,18 @@
|
||||
# 桃夭
|
||||
|
||||
基于WebRTC实现信令服务,实现Mesh、MCU和SFU三种媒体通信架构,支持直播会议两种场景。
|
||||
项目提供WebRTC服务信令,终端已有H5示例,其他终端需要自己实现。
|
||||
|
||||
## 授权
|
||||
|
||||
开源公益免费,商用需要购买授权。
|
||||
|
||||
## 模块
|
||||
|
||||
|模块|名称|描述|
|
||||
|:--|:--|:--|
|
||||
|taoyao|桃夭|桃之夭夭灼灼其华|
|
||||
|taoyao-nat|内网穿透|STUN/TURN|
|
||||
|taoyao-boot|基础|启动模块|
|
||||
|taoyao-boot|启动模块|基础模块|
|
||||
|taoyao-live|直播|直播、连麦|
|
||||
|taoyao-test|测试|测试工具|
|
||||
|taoyao-media|媒体|录制、视频(美颜、AI识别)、音频(混音、变声、降噪)|
|
||||
@@ -16,12 +20,41 @@
|
||||
|taoyao-server|服务|启动服务|
|
||||
|taoyao-meeting|会议|会议模式、广播模式、单人对讲|
|
||||
|taoyao-webrtc|WebRTC模块|WebRTC模块|
|
||||
|taoyao-webrtc-jni|WebRTC JNI|WebRTC本地接口|
|
||||
|taoyao-webrtc-sfu|WebRTC SFU架构|SFU架构|
|
||||
|taoyao-webrtc-mcu|WebRTC MCU架构|MCU架构|
|
||||
|taoyao-webrtc-mesh|WebRTC MESH架构|MESH架构|
|
||||
|taoyao-webrtc-jitsi|WebRTC协议簇jitsi实现|WebRTC协议簇jitsi实现|
|
||||
|taoyao-webrtc-kurento|WebRTC协议簇kurento实现|WebRTC协议簇kurento实现|
|
||||
|
||||
## STUN/TURN公共服务
|
||||
> 终端负责推流,服务端负责处理媒体流,这些功能也可以在终端实现。主次码流没在终端实现,服务端实现可以有更多选择。
|
||||
|
||||
## 模块关系
|
||||
|
||||
```
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| taoyao-server |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| taoyao-live | taoyao-meeting |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| taoyao-signal |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| taoyao-media |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| taoyao-mcu | taoyao-sfu | |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ taoyao-mesh +
|
||||
| taoyao-jitsi | taoyao-kurento | |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| taoyao-boot |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
```
|
||||
|
||||
## 内网穿透
|
||||
|
||||
请用公共STUN/TURN服务或者自行搭建coturn服务。
|
||||
|
||||
> 只有公网Mesh架构才需要真正的内网穿透
|
||||
|
||||
### STUN/TURN公共服务地址
|
||||
|
||||
```
|
||||
stun:stun1.l.google.com:19302
|
||||
@@ -33,20 +66,35 @@ stun:stun.stunprotocol.org:3478
|
||||
|
||||
## 信令
|
||||
|
||||
|功能|描述|
|
||||
|:--|:--|
|
||||
|注册|终端注册(同步信息)|
|
||||
|关闭|终端关闭(注销)|
|
||||
|心跳|终端心跳|
|
||||
|进入会议|没有会议自动创建|
|
||||
|离开会议|离开会议|
|
||||
|关闭会议|关闭会议(所有人员离开)|
|
||||
|邀请终端|会议邀请终端|
|
||||
|踢出终端|会议踢出终端|
|
||||
|推流|控制终端推流|
|
||||
|暂停推流|控制终端暂停推流|
|
||||
|订阅(分流)|控制终端暂停推流|
|
||||
|暂停订阅(分流)|控制终端暂停推流|
|
||||
|功能|描述|标识|响应|
|
||||
|:--|:--|:--|:--|
|
||||
|注册|终端注册(同步信息)|||
|
||||
|关闭|终端关闭(注销)|||
|
||||
|心跳|终端心跳|||
|
||||
|创建会议|创建会议||返回会议ID|
|
||||
|进入会议|没有会议自动创建||返回会议终端同时广播进入消息|
|
||||
|离开会议|离开会议||广播离开消息|
|
||||
|关闭会议|关闭会议(踢出所有人员)||广播关闭消息|
|
||||
|终端列表|||返回所有终端列表|
|
||||
|会议终端列表|||返回所有会议终端列表|
|
||||
|直播终端列表|||返回所有直播终端列表|
|
||||
|邀请终端|会议邀请终端(主动/被动)||单播邀请|
|
||||
|踢出终端|会议踢出终端||单播踢出|
|
||||
|开启直播||||
|
||||
|关闭直播||||
|
||||
|发布|控制终端推流|||
|
||||
|取消发布|控制终端暂停推流|||
|
||||
|订阅|订阅终端媒体流|||
|
||||
|取消订阅|取消订阅终端媒体流|||
|
||||
|暂停媒体流|暂停终端媒体流分流(不关媒体流通道)|||
|
||||
|恢复媒体流|恢复终端媒体流分流(不关媒体流通道)|||
|
||||
|开启录像||||
|
||||
|关闭录像||||
|
||||
|终端状态||||
|
||||
|单播消息|发送指定终端|||
|
||||
|广播消息|广播排除自己的所有终端|||
|
||||
|全员广播消息|广播包括自己的所有终端|||
|
||||
|异常|异常信息|||
|
||||
|
||||
## 直播
|
||||
|
||||
@@ -58,6 +106,8 @@ stun:stun.stunprotocol.org:3478
|
||||
|
||||
流媒体点对点连接,不经过服务端。
|
||||
|
||||
> 录制、AI识别等等功能只能在终端实现。
|
||||
|
||||
### MCU
|
||||
|
||||
终端推流到服务端,由服务端分流并且混音。
|
||||
@@ -70,4 +120,4 @@ stun:stun.stunprotocol.org:3478
|
||||
|
||||
```
|
||||
keytool -genkeypair -keyalg RSA -dname "CN=localhost, OU=acgist, O=taoyao, L=GZ, ST=GD, C=CN" -alias taoyao -validity 3650 -ext ku:c=dig,keyE -ext eku=serverAuth -ext SAN=dns:localhost,ip:127.0.0.1 -keystore taoyao.jks -keypass 123456 -storepass 123456
|
||||
```
|
||||
```
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${system.maven.basedir}/docs</directory>
|
||||
<directory>${taoyao.maven.basedir}/docs</directory>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<includes>
|
||||
<include>README.md</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${system.maven.basedir}/docs/bin</directory>
|
||||
<directory>${taoyao.maven.basedir}/docs/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<fileMode>0755</fileMode>
|
||||
<filtered>true</filtered>
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${system.maven.basedir}/docs</directory>
|
||||
<directory>${taoyao.maven.basedir}/docs</directory>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<includes>
|
||||
<include>README.md</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${system.maven.basedir}/docs/bin</directory>
|
||||
<directory>${taoyao.maven.basedir}/docs/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<fileMode>0755</fileMode>
|
||||
<filtered>true</filtered>
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${system.maven.basedir}/docs</directory>
|
||||
<directory>${taoyao.maven.basedir}/docs</directory>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<includes>
|
||||
<include>README.md</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${system.maven.basedir}/docs/bin</directory>
|
||||
<directory>${taoyao.maven.basedir}/docs/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<fileMode>0755</fileMode>
|
||||
<filtered>true</filtered>
|
||||
|
||||
@@ -20,10 +20,10 @@ fi
|
||||
|
||||
# 启动参数
|
||||
JAVA_OPTS_GC="-XX:+UseG1GC -Xlog:gc:./logs/gc.log:time,level"
|
||||
JAVA_OPTS_MEM="-server ${system.maven.jvm.mem}"
|
||||
JAVA_OPTS_EXT="-Dfile.encoding=${system.maven.encoding} -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true"
|
||||
JAVA_OPTS_MEM="-server ${taoyao.maven.jvm.mem}"
|
||||
JAVA_OPTS_EXT="-Dfile.encoding=${taoyao.maven.encoding} -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true"
|
||||
JAVA_OPTS_APP="-Dspring.profiles.active=${profile}"
|
||||
JAVA_OPTS="$JAVA_OPTS_MEM $JAVA_OPTS_EXT $JAVA_OPTS_APP ${system.maven.jvm.arg}"
|
||||
JAVA_OPTS="$JAVA_OPTS_MEM $JAVA_OPTS_EXT $JAVA_OPTS_APP ${taoyao.maven.jvm.arg}"
|
||||
echo "启动参数:$JAVA_OPTS"
|
||||
|
||||
# 启动应用
|
||||
|
||||
BIN
docs/taoyao-v1.0.zip
Normal file
BIN
docs/taoyao-v1.0.zip
Normal file
Binary file not shown.
72
pom.xml
72
pom.xml
@@ -24,18 +24,19 @@
|
||||
<!-- 版本 -->
|
||||
<java.version>17</java.version>
|
||||
<lombok.version>1.18.24</lombok.version>
|
||||
<webrtc.version>1.0.32006</webrtc.version>
|
||||
<kurento.version>6.18.0</kurento.version>
|
||||
<springdoc.version>1.6.12</springdoc.version>
|
||||
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
||||
<collections4.version>4.4</collections4.version>
|
||||
<jitsi.ice4j.version>3.0-59-g71e244d</jitsi.ice4j.version>
|
||||
<jitsi.libjitsi.version>1.1-22-g5c9346c5</jitsi.libjitsi.version>
|
||||
<!-- 配置 -->
|
||||
<system.maven.basedir>${project.basedir}</system.maven.basedir>
|
||||
<system.maven.encoding>UTF-8</system.maven.encoding>
|
||||
<system.maven.skip.assembly>true</system.maven.skip.assembly>
|
||||
<taoyao.maven.basedir>${project.basedir}</taoyao.maven.basedir>
|
||||
<taoyao.maven.encoding>UTF-8</taoyao.maven.encoding>
|
||||
<taoyao.maven.skip.assembly>true</taoyao.maven.skip.assembly>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>taoyao-nat</module>
|
||||
<module>taoyao-boot</module>
|
||||
<module>taoyao-live</module>
|
||||
<module>taoyao-test</module>
|
||||
@@ -103,11 +104,6 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-nat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-boot</artifactId>
|
||||
@@ -148,11 +144,6 @@
|
||||
<artifactId>taoyao-webrtc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-jni</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-sfu</artifactId>
|
||||
@@ -168,11 +159,32 @@
|
||||
<artifactId>taoyao-webrtc-mesh</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- WebRTC -->
|
||||
<dependency>
|
||||
<groupId>org.webrtc</groupId>
|
||||
<artifactId>google-webrtc</artifactId>
|
||||
<version>${webrtc.version}</version>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-jitsi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-kurento</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- WebRTC:jitsi -->
|
||||
<dependency>
|
||||
<groupId>org.jitsi</groupId>
|
||||
<artifactId>ice4j</artifactId>
|
||||
<version>${jitsi.ice4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jitsi</groupId>
|
||||
<artifactId>libjitsi</artifactId>
|
||||
<version>${jitsi.libjitsi.version}</version>
|
||||
</dependency>
|
||||
<!-- WebRTC:kurento -->
|
||||
<dependency>
|
||||
<groupId>org.kurento</groupId>
|
||||
<artifactId>kurento-client</artifactId>
|
||||
<version>${kurento.version}</version>
|
||||
</dependency>
|
||||
<!-- 集合工具 -->
|
||||
<dependency>
|
||||
@@ -243,7 +255,7 @@
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>${system.maven.encoding}</encoding>
|
||||
<encoding>${taoyao.maven.encoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -276,7 +288,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<attach>false</attach>
|
||||
<skipAssembly>${system.maven.skip.assembly}</skipAssembly>
|
||||
<skipAssembly>${taoyao.maven.skip.assembly}</skipAssembly>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
</configuration>
|
||||
</execution>
|
||||
@@ -294,8 +306,8 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<profile>dev</profile>
|
||||
<system.maven.jvm.arg></system.maven.jvm.arg>
|
||||
<system.maven.jvm.mem>-Xms512M -Xmx1024M -XX:NewRatio=1 -XX:SurvivorRatio=2</system.maven.jvm.mem>
|
||||
<taoyao.maven.jvm.arg></taoyao.maven.jvm.arg>
|
||||
<taoyao.maven.jvm.mem>-Xms512M -Xmx1024M -XX:NewRatio=1 -XX:SurvivorRatio=2</taoyao.maven.jvm.mem>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -304,7 +316,7 @@
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${system.maven.basedir}/docs/assembly/dev.xml</descriptor>
|
||||
<descriptor>${taoyao.maven.basedir}/docs/assembly/dev.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -324,8 +336,8 @@
|
||||
<id>test</id>
|
||||
<properties>
|
||||
<profile>test</profile>
|
||||
<system.maven.jvm.arg></system.maven.jvm.arg>
|
||||
<system.maven.jvm.mem>-Xms512M -Xmx1024M -XX:NewRatio=1 -XX:SurvivorRatio=2</system.maven.jvm.mem>
|
||||
<taoyao.maven.jvm.arg></taoyao.maven.jvm.arg>
|
||||
<taoyao.maven.jvm.mem>-Xms512M -Xmx1024M -XX:NewRatio=1 -XX:SurvivorRatio=2</taoyao.maven.jvm.mem>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -334,7 +346,7 @@
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${system.maven.basedir}/docs/assembly/test.xml</descriptor>
|
||||
<descriptor>${taoyao.maven.basedir}/docs/assembly/test.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -355,8 +367,8 @@
|
||||
<id>release</id>
|
||||
<properties>
|
||||
<profile>release</profile>
|
||||
<system.maven.jvm.arg>-Dtaoyao.password=123456</system.maven.jvm.arg>
|
||||
<system.maven.jvm.mem>-Xms2048M -Xmx4096M -XX:NewRatio=1 -XX:SurvivorRatio=2</system.maven.jvm.mem>
|
||||
<taoyao.maven.jvm.arg>-Dtaoyao.password=123456</taoyao.maven.jvm.arg>
|
||||
<taoyao.maven.jvm.mem>-Xms2048M -Xmx4096M -XX:NewRatio=1 -XX:SurvivorRatio=2</taoyao.maven.jvm.mem>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -365,7 +377,7 @@
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${system.maven.basedir}/docs/assembly/release.xml</descriptor>
|
||||
<descriptor>${taoyao.maven.basedir}/docs/assembly/release.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -75,7 +75,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
@EnableConfigurationProperties({ IdProperties.class, TaoyaoProperties.class, WebrtcProperties.class, SecurityProperties.class })
|
||||
@EnableConfigurationProperties({ IdProperties.class, MediaProperties.class, TaoyaoProperties.class, WebrtcProperties.class, SecurityProperties.class })
|
||||
public class BootAutoConfiguration {
|
||||
|
||||
@Value("${spring.application.name:taoyao}")
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 音频配置
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(title = "音频配置", description = "音频配置")
|
||||
public class MediaAudioProperties {
|
||||
|
||||
/**
|
||||
* 音频格式
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public enum Format {
|
||||
|
||||
/**
|
||||
* ACC
|
||||
*/
|
||||
ACC,
|
||||
/**
|
||||
* PCM
|
||||
*/
|
||||
PCM,
|
||||
/**
|
||||
* OPUS
|
||||
*/
|
||||
OPUS;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式
|
||||
*/
|
||||
@Schema(title = "格式", description = "格式")
|
||||
private Format format;
|
||||
/**
|
||||
* 采样率
|
||||
* 8000|16000|32000|48000
|
||||
*/
|
||||
@Schema(title = "采样率", description = "采样率", example = "8000|16000|32000|48000")
|
||||
private Integer samplerate;
|
||||
/**
|
||||
* 采样数
|
||||
*/
|
||||
@Schema(title = "采样数", description = "采样数", example = "16")
|
||||
private Integer samplesize;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 媒体配置
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(title = "媒体配置", description = "媒体配置")
|
||||
@ConfigurationProperties(prefix = "taoyao.media")
|
||||
public class MediaProperties {
|
||||
|
||||
/**
|
||||
* 音频配置
|
||||
*/
|
||||
private MediaAudioProperties audio;
|
||||
/**
|
||||
* 视频配置
|
||||
*/
|
||||
private MediaVideoProperties video;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.acgist.taoyao.boot.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 视频配置
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(title = "视频配置", description = "视频配置")
|
||||
public class MediaVideoProperties {
|
||||
|
||||
/**
|
||||
* 视频格式
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public enum Format {
|
||||
|
||||
/**
|
||||
* VP8
|
||||
*/
|
||||
VP8,
|
||||
/**
|
||||
* VP9
|
||||
*/
|
||||
VP9,
|
||||
/**
|
||||
* H264
|
||||
*/
|
||||
H264,
|
||||
/**
|
||||
* H265
|
||||
*/
|
||||
H265;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式
|
||||
*/
|
||||
@Schema(title = "格式", description = "格式")
|
||||
private Format format;
|
||||
/**
|
||||
* 码率(画质)
|
||||
*/
|
||||
@Schema(title = "码率", description = "码率影响画质", example = "600|1200|1500|1800")
|
||||
private Integer bitrate;
|
||||
/**
|
||||
* 帧率(流畅)
|
||||
*/
|
||||
@Schema(title = "帧率", description = "帧率影响流程", example = "20|24|30|60")
|
||||
private Integer framerate;
|
||||
/**
|
||||
* 分辨率(画面大小)
|
||||
*/
|
||||
@Schema(title = "分辨率", description = "分辨率影响画面大小", example = "1920*1080|1280*720|480*360")
|
||||
private String resolution;
|
||||
/**
|
||||
* 宽度
|
||||
*/
|
||||
@Schema(title = "宽度", description = "宽度")
|
||||
private Integer width;
|
||||
/**
|
||||
* 高度
|
||||
*/
|
||||
@Schema(title = "高度", description = "高度")
|
||||
private Integer height;
|
||||
|
||||
/**
|
||||
* @return 宽度
|
||||
*/
|
||||
public Integer getWidth() {
|
||||
if(this.width == null) {
|
||||
final int index = this.resolution.indexOf('*');
|
||||
this.width = Integer.valueOf(this.resolution.substring(0, index).strip());
|
||||
}
|
||||
return this.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 高度
|
||||
*/
|
||||
public Integer getHeight() {
|
||||
if(this.height == null) {
|
||||
final int index = this.resolution.indexOf('*');
|
||||
this.height = Integer.valueOf(this.resolution.substring(index + 1).strip());
|
||||
}
|
||||
return this.height;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class SecurityProperties {
|
||||
*/
|
||||
private String[] permit;
|
||||
/**
|
||||
* 用户
|
||||
* 名称
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
|
||||
@@ -18,11 +18,11 @@ import lombok.Setter;
|
||||
public class WebrtcProperties {
|
||||
|
||||
/**
|
||||
* 架构类型
|
||||
* 架构模型
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public enum Type {
|
||||
public enum Model {
|
||||
|
||||
/**
|
||||
* SFU架构
|
||||
@@ -40,10 +40,33 @@ public class WebrtcProperties {
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型
|
||||
* 基础框架
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Schema(title = "架构类型", description = "WebRTC架构类型")
|
||||
private Type type;
|
||||
public enum Framework {
|
||||
|
||||
/**
|
||||
* jitsi
|
||||
*/
|
||||
JITSI,
|
||||
/**
|
||||
* kurento
|
||||
*/
|
||||
KURENTO;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 模型
|
||||
*/
|
||||
@Schema(title = "架构模型", description = "WebRTC架构模型")
|
||||
private Model model;
|
||||
/**
|
||||
* 框架
|
||||
*/
|
||||
@Schema(title = "基础框架", description = "WebRTC基础框架")
|
||||
private Framework framework;
|
||||
/**
|
||||
* stun服务器
|
||||
*/
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="false" scanPeriod="60 seconds" debug="false">
|
||||
|
||||
<springProperty scope="context" name="system.name" source="spring.application.name" />
|
||||
<springProperty scope="context" name="log.name" source="spring.application.name" />
|
||||
|
||||
<contextName>${system.name}</contextName>
|
||||
<property name="system.path" value="logs" />
|
||||
<property name="system.queue" value="2048" />
|
||||
<property name="system.buffer" value="8192" />
|
||||
<property name="system.history" value="30" />
|
||||
<property name="system.charset" value="UTF-8" />
|
||||
<property name="system.pattern" value="[${system.name}] %d{YYYY-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} %file:%line - %m%n" />
|
||||
<contextName>${log.name}</contextName>
|
||||
<property name="log.path" value="logs" />
|
||||
<property name="log.queue" value="2048" />
|
||||
<property name="log.buffer" value="8192" />
|
||||
<property name="log.history" value="30" />
|
||||
<property name="log.charset" value="UTF-8" />
|
||||
<property name="log.pattern" value="[${log.name}] %d{YYYY-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} %file:%line - %m%n" />
|
||||
|
||||
<appender name="fileDebug" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${system.path}/${system.name}.debug.log</file>
|
||||
<file>${log.path}/${log.name}.debug.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<maxHistory>${system.history}</maxHistory>
|
||||
<fileNamePattern>${system.path}/%d{yyyy-MM, aux}/${system.name}.debug.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
<maxHistory>${log.history}</maxHistory>
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/${log.name}.debug.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<bufferSize>${system.buffer}</bufferSize>
|
||||
<bufferSize>${log.buffer}</bufferSize>
|
||||
<immediateFlush>false</immediateFlush>
|
||||
<encoder>
|
||||
<charset>${system.charset}</charset>
|
||||
<pattern>${system.pattern}</pattern>
|
||||
<charset>${log.charset}</charset>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
@@ -29,22 +29,22 @@
|
||||
</appender>
|
||||
<appender name="fileDebugAsync" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="fileDebug" />
|
||||
<queueSize>${system.queue}</queueSize>
|
||||
<queueSize>${log.queue}</queueSize>
|
||||
<includeCallerData>true</includeCallerData>
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
</appender>
|
||||
|
||||
<appender name="fileInfo" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${system.path}/${system.name}.info.log</file>
|
||||
<file>${log.path}/${log.name}.info.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<maxHistory>${system.history}</maxHistory>
|
||||
<fileNamePattern>${system.path}/%d{yyyy-MM, aux}/${system.name}.info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
<maxHistory>${log.history}</maxHistory>
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/${log.name}.info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<bufferSize>${system.buffer}</bufferSize>
|
||||
<bufferSize>${log.buffer}</bufferSize>
|
||||
<immediateFlush>false</immediateFlush>
|
||||
<encoder>
|
||||
<charset>${system.charset}</charset>
|
||||
<pattern>${system.pattern}</pattern>
|
||||
<charset>${log.charset}</charset>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
@@ -52,22 +52,22 @@
|
||||
</appender>
|
||||
<appender name="fileInfoAsync" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="fileInfo" />
|
||||
<queueSize>${system.queue}</queueSize>
|
||||
<queueSize>${log.queue}</queueSize>
|
||||
<includeCallerData>true</includeCallerData>
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
</appender>
|
||||
|
||||
<appender name="fileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${system.path}/${system.name}.error.log</file>
|
||||
<file>${log.path}/${log.name}.error.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<maxHistory>${system.history}</maxHistory>
|
||||
<fileNamePattern>${system.path}/%d{yyyy-MM, aux}/${system.name}.error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
<maxHistory>${log.history}</maxHistory>
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/${log.name}.error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<bufferSize>${system.buffer}</bufferSize>
|
||||
<bufferSize>${log.buffer}</bufferSize>
|
||||
<immediateFlush>false</immediateFlush>
|
||||
<encoder>
|
||||
<charset>${system.charset}</charset>
|
||||
<pattern>${system.pattern}</pattern>
|
||||
<charset>${log.charset}</charset>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
@@ -75,15 +75,15 @@
|
||||
</appender>
|
||||
<appender name="fileErrorAsync" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="fileError" />
|
||||
<queueSize>${system.queue}</queueSize>
|
||||
<queueSize>${log.queue}</queueSize>
|
||||
<includeCallerData>true</includeCallerData>
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
</appender>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<charset>${system.charset}</charset>
|
||||
<pattern>${system.pattern}</pattern>
|
||||
<charset>${log.charset}</charset>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-signal</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -14,8 +14,29 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>taoyao-media</name>
|
||||
<description>媒体:录制、视频(美颜、AI识别)、音频(混音、变声)</description>
|
||||
<description>媒体:录制、视频(美颜、AI识别)、音频(混音、变声、降噪)</description>
|
||||
|
||||
<dependencies></dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-mcu</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-sfu</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-mesh</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-jitsi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-kurento</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -18,13 +18,8 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-signal</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.acgist.taoyao.meeting.room;
|
||||
|
||||
/**
|
||||
* 房间
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class Room {
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# 内网穿透
|
||||
|
||||
请用公共STUN/TURN服务或者自行搭建coturn服务。
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>taoyao-nat</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>taoyao-net</name>
|
||||
<description>内网穿透:STUN/TURN</description>
|
||||
|
||||
<dependencies></dependencies>
|
||||
|
||||
</project>
|
||||
@@ -17,8 +17,8 @@
|
||||
<description>服务:启动服务</description>
|
||||
|
||||
<properties>
|
||||
<system.maven.basedir>${project.parent.basedir}</system.maven.basedir>
|
||||
<system.maven.skip.assembly>false</system.maven.skip.assembly>
|
||||
<taoyao.maven.basedir>${project.parent.basedir}</taoyao.maven.basedir>
|
||||
<taoyao.maven.skip.assembly>false</taoyao.maven.skip.assembly>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -34,18 +34,6 @@
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-meeting</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-sfu</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-mcu</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-mesh</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.acgist.taoyao.boot.config.MediaProperties;
|
||||
import com.acgist.taoyao.boot.config.WebrtcProperties;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -20,9 +21,17 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@RequestMapping("/config")
|
||||
public class ConfigController {
|
||||
|
||||
@Autowired
|
||||
private MediaProperties mediaProperties;
|
||||
@Autowired
|
||||
private WebrtcProperties webrtcProperties;
|
||||
|
||||
@Operation(summary = "媒体配置", description = "媒体配置")
|
||||
@GetMapping("/media")
|
||||
public MediaProperties media() {
|
||||
return this.mediaProperties;
|
||||
}
|
||||
|
||||
@Operation(summary = "WebRTC配置", description = "WebRTC配置")
|
||||
@GetMapping("/webrtc")
|
||||
public WebrtcProperties webrtc() {
|
||||
|
||||
@@ -53,8 +53,20 @@ taoyao:
|
||||
id:
|
||||
sn: 0
|
||||
max-index: 999999
|
||||
media:
|
||||
audio:
|
||||
format: OPUS
|
||||
samplesize: 16
|
||||
samplerate: 32000
|
||||
video:
|
||||
format: H264
|
||||
bitrate: 1200
|
||||
framerate: 24
|
||||
resolution: 1280*760
|
||||
quality: high|standard|quick
|
||||
webrtc:
|
||||
type: SFU
|
||||
model: SFU
|
||||
framework: JITSI
|
||||
stun:
|
||||
- stun:stun1.l.google.com:19302
|
||||
- stun:stun2.l.google.com:19302
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>终端</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
1
taoyao-server/src/main/resources/static/css/font.min.css
vendored
Normal file
1
taoyao-server/src/main/resources/static/css/font.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
36
taoyao-server/src/main/resources/static/css/style.css
Normal file
36
taoyao-server/src/main/resources/static/css/style.css
Normal file
@@ -0,0 +1,36 @@
|
||||
@charset "UTF-8";
|
||||
/**文本选择*/
|
||||
::selection{background:#222;color:#fff;}
|
||||
/**字体大小*/
|
||||
@media screen and (min-width:800px){html{font-size:16px;}}
|
||||
@media screen and (min-width:1200px){html{font-size:18px;}}
|
||||
@media screen and (min-width:1600px){html{font-size:20px;}}
|
||||
/**默认样式*/
|
||||
*{margin:0;padding:0;border:none;outline:none;box-sizing:content-box;}
|
||||
html{background:#EBEBEB;}
|
||||
html,body{font-family:Arial,Consolas,SimSun,"宋体";color:#222;font-weight:normal;}
|
||||
body{width:100%;height:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);font-size:1rem;line-height:1.4em;}
|
||||
a{color:#1155AA;text-decoration:none;}
|
||||
a:link{text-decoration:none;}
|
||||
a:hover{color:#4477EE;text-decoration:none;}
|
||||
a:visited{text-decoration:none;}
|
||||
img{border:0;}
|
||||
ol,ul,li{list-style:none;}
|
||||
input[type=text],textarea{box-shadow:0px 0px 3px 0px rgba(0,0,0,0.1) inset;border:1px solid rgba(0,0,0,0.1)!important;}
|
||||
input[type=text]:focus,textarea:focus,input[type=text]:hover,textarea:hover{border:1px solid #1155AA!important;}
|
||||
input::-webkit-calendar-picker-indicator{color:#1155AA;background:none;}
|
||||
/**容器*/
|
||||
.taoyao{text-align:center;}
|
||||
/**直播*/
|
||||
.taoyao .live > .video{width:100%;height:100%;}
|
||||
.taoyao .live .handler{position:fixed;width:100%;bottom:2rem;font-size:2rem;}
|
||||
/**会议*/
|
||||
.taoyao .handler a{cursor:pointer;}
|
||||
.taoyao > .handler{font-size:2rem;padding:1rem 0;width:100%;}
|
||||
.taoyao .list{width:90vw;margin:auto;}
|
||||
.taoyao .meeting{float:left;overflow:hidden;position:relative;width:calc(25% - 2rem);border:1rem solid #fff;}
|
||||
.taoyao .me,.taoyao .meeting:hover{border-color:#060;}
|
||||
.taoyao .meeting > .video{height:15vw;}
|
||||
.taoyao .meeting > .video video{width:100%;height:100%;}
|
||||
.taoyao .meeting .handler{position:absolute;bottom:0rem;text-align:center;width:100%;background:rgba(0,0,0,0.2);padding:0.2rem 0;}
|
||||
.taoyao .meeting .handler a{color:#fff;}
|
||||
501
taoyao-server/src/main/resources/static/fonts/taoyao.svg
Normal file
501
taoyao-server/src/main/resources/static/fonts/taoyao.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 297 KiB |
BIN
taoyao-server/src/main/resources/static/fonts/taoyao.ttf
Normal file
BIN
taoyao-server/src/main/resources/static/fonts/taoyao.ttf
Normal file
Binary file not shown.
BIN
taoyao-server/src/main/resources/static/fonts/taoyao.woff
Normal file
BIN
taoyao-server/src/main/resources/static/fonts/taoyao.woff
Normal file
Binary file not shown.
@@ -3,8 +3,16 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>桃夭</title>
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css" />
|
||||
<script type="text/javascript" src="./javascript/taoyao.js"></script>
|
||||
<style type="text/css">
|
||||
a{width:50%;height:100%;position:fixed;text-align:center;line-height:100%;font-size:4rem;display:flex;align-items:center;justify-content:center;}
|
||||
a:last-child{left:50%;}
|
||||
a:hover{color:#fff;background:#060;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a href="./live.html">直播</a>
|
||||
<a href="./meeting.html">会议</a>
|
||||
</body>
|
||||
</html>
|
||||
327
taoyao-server/src/main/resources/static/javascript/taoyao.js
Normal file
327
taoyao-server/src/main/resources/static/javascript/taoyao.js
Normal file
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* 桃夭WebRTC终端示例
|
||||
*/
|
||||
/** 音频配置 */
|
||||
const defaultAudioConfig = {
|
||||
// 音量:0~1
|
||||
volume: 0.5,
|
||||
// 设备
|
||||
// deviceId : '',
|
||||
// 采样率:8000|16000|32000|48000
|
||||
sampleRate: 32000,
|
||||
// 采样数:16
|
||||
sampleSize: 16,
|
||||
// 延迟大小(单位毫秒):500毫秒以内较好
|
||||
latency: 0.3,
|
||||
// 声道数量:1|2
|
||||
channelCount : 1,
|
||||
// 是否开启自动增益:true|false
|
||||
autoGainControl: false,
|
||||
// 是否开启降噪功能:true|false
|
||||
noiseSuppression: true,
|
||||
// 是否开启回音消除:true|false
|
||||
echoCancellation: true,
|
||||
// 消除回音方式:system|browser
|
||||
echoCancellationType: 'system'
|
||||
};
|
||||
/** 视频配置 */
|
||||
const defaultVideoConfig = {
|
||||
// 宽度
|
||||
width: 1280,
|
||||
// 高度
|
||||
height: 720,
|
||||
// 设备
|
||||
// deviceId: '',
|
||||
// 帧率
|
||||
frameRate: 30,
|
||||
// 裁切
|
||||
// resizeMode: '',
|
||||
// 选摄像头:user|left|right|environment
|
||||
facingMode: 'environment'
|
||||
}
|
||||
/** 兼容 */
|
||||
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
|
||||
/** 桃夭 */
|
||||
function Taoyao(
|
||||
webSocket,
|
||||
iceServer
|
||||
) {
|
||||
this.webSocket = webSocket;
|
||||
this.iceServer = iceServer;
|
||||
this.audioStatus = true;
|
||||
this.videoStatus = true;
|
||||
this.audioStreamId = null;
|
||||
this.videoStreamId = null;
|
||||
this.audioConfig = defaultAudioConfig;
|
||||
this.videoConfig = defaultVideoConfig;
|
||||
/** 初始 */
|
||||
this.init = function() {
|
||||
if(navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
|
||||
navigator.mediaDevices.enumerateDevices()
|
||||
.then(list => {
|
||||
let audioDevice = false;
|
||||
let videoDevice = false;
|
||||
list.forEach(v => {
|
||||
console.log('终端媒体设备', v.kind, v.label);
|
||||
if(v.kind === 'audioinput') {
|
||||
audioDevice = true;
|
||||
} else if(v.kind === 'videoinput') {
|
||||
videoDevice = true;
|
||||
}
|
||||
});
|
||||
if(!audioDevice) {
|
||||
console.log('终端没有音频输入设备');
|
||||
this.audioConfig = false;
|
||||
}
|
||||
if(!videoDevice) {
|
||||
console.log('终端没有视频输入设备');
|
||||
this.videoConfig = false;
|
||||
}
|
||||
})
|
||||
.catch(e => console.log('获取终端设备失败', e));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
/** 媒体 */
|
||||
this.buildUserMedia = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log("获取终端媒体:", this.audioConfig, this.videoConfig);
|
||||
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: this.audioConfig,
|
||||
video: this.videoConfig
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
} else if(navigator.getUserMedia) {
|
||||
navigator.getUserMedia({
|
||||
audio: this.audioConfig,
|
||||
video: this.videoConfig
|
||||
}, resolve, reject);
|
||||
} else {
|
||||
reject("获取终端媒体失败");
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 本地 */
|
||||
this.local = async function(localVideoId, stream) {
|
||||
const localVideo = document.getElementById(localVideoId);
|
||||
if ('srcObject' in localVideo) {
|
||||
localVideo.srcObject = stream;
|
||||
} else {
|
||||
localVideo.src = URL.createObjectURL(stream);;
|
||||
}
|
||||
await localVideo.play();
|
||||
};
|
||||
/** 连接 */
|
||||
this.connect = function() {
|
||||
};
|
||||
/** 重连 */
|
||||
/** 定时 */
|
||||
/** 媒体 */
|
||||
/** 视频 */
|
||||
/** 心跳 */
|
||||
}
|
||||
/*
|
||||
var peer;
|
||||
var socket; // WebSocket
|
||||
var supportStream = false; // 是否支持使用数据流
|
||||
var localVideo; // 本地视频
|
||||
var localVideoStream; // 本地视频流
|
||||
var remoteVideo; // 远程视频
|
||||
var remoteVideoStream; // 远程视频流
|
||||
var initiator = false; // 是否已经有人在等待
|
||||
var started = false; // 是否开始
|
||||
var channelReady = false; // 是否打开WebSocket通道
|
||||
// 初始
|
||||
function initialize() {
|
||||
console.log("初始聊天");
|
||||
// 获取视频
|
||||
localVideo = document.getElementById("localVideo");
|
||||
remoteVideo = document.getElementById("remoteVideo");
|
||||
supportStream = "srcObject" in localVideo;
|
||||
// 显示状态
|
||||
if (initiator) {
|
||||
setNotice("开始连接");
|
||||
} else {
|
||||
setNotice("加入聊天:https://www.acgist.com/demo/video/?oid=FFB85D84AC56DAF88B7E22AFFA7533D3");
|
||||
}
|
||||
// 打开WebSocket
|
||||
openChannel();
|
||||
// 创建终端媒体
|
||||
buildUserMedia();
|
||||
}
|
||||
function openChannel() {
|
||||
console.log("打开WebSocket");
|
||||
socket = new WebSocket("wss://www.acgist.com/video.ws/FFB85D84AC56DAF88B7E22AFFA7533D3");
|
||||
socket.onopen = channelOpened;
|
||||
socket.onmessage = channelMessage;
|
||||
socket.onclose = channelClosed;
|
||||
socket.onerror = channelError;
|
||||
}
|
||||
function channelOpened() {
|
||||
console.log("打开WebSocket成功");
|
||||
channelReady = true;
|
||||
}
|
||||
function channelMessage(message) {
|
||||
console.log("收到消息:" + message.data);
|
||||
var msg = JSON.parse(message.data);
|
||||
if (msg.type === "offer") { // 处理Offer消息
|
||||
if (!initiator && !started) {
|
||||
connectPeer();
|
||||
}
|
||||
peer.setRemoteDescription(new RTCSessionDescription(msg));
|
||||
peer.createAnswer().then(buildLocalDescription);
|
||||
} else if (msg.type === "answer" && started) { // 处理Answer消息
|
||||
peer.setRemoteDescription(new RTCSessionDescription(msg));
|
||||
} else if (msg.type === "candidate" && started) {
|
||||
var candidate = new RTCIceCandidate({
|
||||
sdpMLineIndex : msg.label,
|
||||
candidate : msg.candidate
|
||||
});
|
||||
peer.addIceCandidate(candidate);
|
||||
} else if (msg.type === "bye" && started) {
|
||||
onRemoteClose();
|
||||
setNotice("对方已断开!");
|
||||
} else if(msg.type === "nowaiting") {
|
||||
onRemoteClose();
|
||||
setNotice("对方已离开!");
|
||||
}
|
||||
}
|
||||
function channelClosed() {
|
||||
console.log("关闭WebSocket");
|
||||
openChannel(); // 重新打开WebSocket
|
||||
}
|
||||
function channelError(event) {
|
||||
console.log("WebSocket异常:" + event);
|
||||
}
|
||||
function buildUserMedia() {
|
||||
console.log("获取终端媒体");
|
||||
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
"audio" : true,
|
||||
"video" : true
|
||||
})
|
||||
.then(onUserMediaSuccess)
|
||||
.catch(onUserMediaError);
|
||||
} else {
|
||||
navigator.getUserMedia({
|
||||
"audio" : true,
|
||||
"video" : true
|
||||
}, onUserMediaSuccess, onUserMediaError);
|
||||
}
|
||||
}
|
||||
function onUserMediaSuccess(stream) {
|
||||
localVideoStream = stream;
|
||||
if (supportStream) {
|
||||
localVideo.srcObject = localVideoStream;
|
||||
} else {
|
||||
localVideo.src = URL.createObjectURL(localVideoStream);
|
||||
}
|
||||
if (initiator) {
|
||||
connectPeer();
|
||||
}
|
||||
}
|
||||
function onUserMediaError(error) {
|
||||
alert("请打开摄像头!");
|
||||
}
|
||||
function connectPeer() {
|
||||
if (!started && localVideoStream && channelReady) {
|
||||
console.log("开始连接Peer");
|
||||
started = true;
|
||||
buildPeerConnection();
|
||||
peer.addStream(localVideoStream);
|
||||
if (initiator) {
|
||||
peer.createOffer().then(buildLocalDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
function buildPeerConnection() {
|
||||
//var server = {"iceServers" : [{"url" : "stun:stun.l.google.com:19302"}]};
|
||||
var server = {"iceServers" : [{"url" : "stun:stun1.l.google.com:19302"}]};
|
||||
peer = new PeerConnection(server);
|
||||
peer.onicecandidate = peerIceCandidate;
|
||||
peer.onconnecting = peerConnecting;
|
||||
peer.onopen = peerOpened;
|
||||
peer.onaddstream = peerAddStream;
|
||||
peer.onremovestream = peerRemoveStream;
|
||||
}
|
||||
function peerIceCandidate(event) {
|
||||
if (event.candidate) {
|
||||
sendMessage({
|
||||
type : "candidate",
|
||||
id : event.candidate.sdpMid,
|
||||
label : event.candidate.sdpMLineIndex,
|
||||
candidate : event.candidate.candidate
|
||||
});
|
||||
} else {
|
||||
console.log("不支持的candidate");
|
||||
}
|
||||
}
|
||||
function peerConnecting(message) {
|
||||
console.log("Peer连接");
|
||||
}
|
||||
function peerOpened(message) {
|
||||
console.log("Peer打开");
|
||||
}
|
||||
function peerAddStream(event) {
|
||||
console.log("远程视频添加");
|
||||
remoteVideoStream = event.stream;
|
||||
if(supportStream) {
|
||||
remoteVideo.srcObject = remoteVideoStream;
|
||||
} else {
|
||||
remoteVideo.src = URL.createObjectURL(remoteVideoStream);
|
||||
}
|
||||
setNotice("连接成功");
|
||||
waitForRemoteVideo();
|
||||
}
|
||||
function peerRemoveStream(event) {
|
||||
console.log("远程视频移除");
|
||||
}
|
||||
function buildLocalDescription(description) {
|
||||
peer.setLocalDescription(description);
|
||||
sendMessage(description);
|
||||
}
|
||||
function sendMessage(message) {
|
||||
var msgJson = JSON.stringify(message);
|
||||
socket.send(msgJson);
|
||||
console.log("发送信息:" + msgJson);
|
||||
}
|
||||
function setNotice(msg) {
|
||||
document.getElementById("footer").innerHTML = msg;
|
||||
}
|
||||
function onRemoteClose() {
|
||||
started = false;
|
||||
initiator = false;
|
||||
if(supportStream) {
|
||||
remoteVideo.srcObject = null;
|
||||
} else {
|
||||
remoteVideo.src = null;
|
||||
}
|
||||
peer.close();
|
||||
}
|
||||
function waitForRemoteVideo() {
|
||||
if (remoteVideo.currentTime > 0) { // 判断远程视频长度
|
||||
setNotice("连接成功!");
|
||||
} else {
|
||||
setTimeout(waitForRemoteVideo, 100);
|
||||
}
|
||||
}
|
||||
window.onbeforeunload = function() {
|
||||
sendMessage({type : "bye"});
|
||||
if(peer) {
|
||||
peer.close();
|
||||
}
|
||||
socket.close();
|
||||
}
|
||||
if(!WebSocket) {
|
||||
alert("你的浏览器不支持WebSocket!");
|
||||
} else if(!PeerConnection) {
|
||||
alert("你的浏览器不支持RTCPeerConnection!");
|
||||
} else {
|
||||
setTimeout(initialize, 100); // 加载完成调用初始化方法
|
||||
}
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
*/
|
||||
28
taoyao-server/src/main/resources/static/live.html
Normal file
28
taoyao-server/src/main/resources/static/live.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>直播</title>
|
||||
<link rel="stylesheet" type="text/css" href="./css/font.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css" />
|
||||
<script type="text/javascript" src="./javascript/taoyao.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="taoyao" id="taoyao">
|
||||
<div class="live">
|
||||
<div class="video">
|
||||
</div>
|
||||
<div class="handler">
|
||||
<a class="audio icon-volume-medium" title="音频状态"></a>
|
||||
<a class="video icon-play2" title="视频状态"></a>
|
||||
<a class="record icon-radio-checked" title="录制视频"></a>
|
||||
<a class="kick icon-cancel-circle" title="退出直播"></a>
|
||||
<a class="close icon-switch" title="关闭直播"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const live = document.getElementById('taoyao');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
60
taoyao-server/src/main/resources/static/meeting.html
Normal file
60
taoyao-server/src/main/resources/static/meeting.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>会议</title>
|
||||
<link rel="stylesheet" type="text/css" href="./css/font.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css" />
|
||||
<script type="text/javascript" src="./javascript/taoyao.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="taoyao">
|
||||
<div class="handler">
|
||||
<a class="create icon-svg" title="创建房间"></a>
|
||||
<a class="invite icon-address-book" title="邀请房间"></a>
|
||||
<a class="enter icon-enter" title="进入房间"></a>
|
||||
<a class="leave icon-exit" title="离开房间"></a>
|
||||
<a class="close icon-switch" title="关闭房间"></a>
|
||||
</div>
|
||||
<div class="list" id="list">
|
||||
<div class="meeting me">
|
||||
<div class="video">
|
||||
<video id="local"></video>
|
||||
</div>
|
||||
<div class="handler">
|
||||
<a class="audio icon-volume-medium" title="音频状态"></a>
|
||||
<a class="video icon-play2" title="视频状态"></a>
|
||||
<a class="record icon-radio-checked" title="录制视频"></a>
|
||||
<a class="kick icon-cancel-circle" title="踢出房间"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const list = document.getElementById('list');
|
||||
const template = `
|
||||
<div class="video">
|
||||
<video></video>
|
||||
</div>
|
||||
<div class="handler">
|
||||
<a class="audio icon-volume-medium" title="音频状态"></a>
|
||||
<a class="video icon-play2" title="视频状态"></a>
|
||||
<a class="record icon-radio-checked" title="录制视频"></a>
|
||||
<a class="kick icon-cancel-circle" title="踢出房间"></a>
|
||||
</div>
|
||||
`;
|
||||
for(let i = 0; i < 10; i++) {
|
||||
const child = document.createElement('div');
|
||||
child.className = 'meeting';
|
||||
child.innerHTML = template;
|
||||
list.appendChild(child);
|
||||
}
|
||||
const taoyao = new Taoyao();
|
||||
taoyao
|
||||
.init()
|
||||
.buildUserMedia()
|
||||
.then(stream => taoyao.local('local', stream))
|
||||
.catch((e) => alert('获取终端媒体失败:' + e));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>房间</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -19,7 +19,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-boot</artifactId>
|
||||
<artifactId>taoyao-media</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.acgist.taoyao.signal.config;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
import com.acgist.taoyao.signal.session.websocket.WebSocketSignal;
|
||||
@@ -13,6 +14,7 @@ import com.acgist.taoyao.signal.session.websocket.WebSocketSignal;
|
||||
* @author acgist
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class SignalAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -9,22 +9,30 @@ public interface ClientMediaHandler {
|
||||
|
||||
/**
|
||||
* 打开
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*/
|
||||
void open(String id);
|
||||
|
||||
/**
|
||||
* 暂停
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*/
|
||||
void pause();
|
||||
void pause(String id);
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*/
|
||||
void resume();
|
||||
void resume(String id);
|
||||
|
||||
/**
|
||||
* 关闭
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*/
|
||||
void close();
|
||||
void close(String id);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,81 @@
|
||||
package com.acgist.taoyao.signal.media;
|
||||
|
||||
/**
|
||||
* 终端推流
|
||||
*/
|
||||
public class ClientMediaPublisher {
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.acgist.taoyao.signal.media.stream.ClientMediaStream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 终端媒体流发布者(终端推流)
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Slf4j
|
||||
public class ClientMediaPublisher implements ClientMediaHandler {
|
||||
|
||||
/**
|
||||
* 发布终端媒体流
|
||||
*/
|
||||
private Map<String, ClientMediaStream> streams = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* 发布
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*
|
||||
* @see #open(String)
|
||||
*/
|
||||
public void publish(String id) {
|
||||
this.open(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消发布
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*
|
||||
* @see #close(String)
|
||||
*/
|
||||
public void unpublish(String id) {
|
||||
this.close(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(String id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause(String id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(String id) {
|
||||
final ClientMediaStream stream = this.streams.get(id);
|
||||
if(stream != null) {
|
||||
try {
|
||||
stream.resume();
|
||||
} catch (IOException e) {
|
||||
log.error("终端媒体流恢复异常:{}", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(String id) {
|
||||
final ClientMediaStream stream = this.streams.get(id);
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("终端媒体流关闭异常:{}", id, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.acgist.taoyao.signal.media;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import com.acgist.taoyao.signal.media.stream.ClientMediaStream;
|
||||
|
||||
/**
|
||||
* 终端媒体订阅者(终端拉流)
|
||||
*
|
||||
@@ -7,10 +12,31 @@ package com.acgist.taoyao.signal.media;
|
||||
*/
|
||||
public class ClientMediaSubscriber implements ClientMediaHandler {
|
||||
|
||||
/**
|
||||
* 订阅终端媒体流
|
||||
*/
|
||||
private List<ClientMediaStream> streams = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* 订阅
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*
|
||||
* @see #open(String)
|
||||
*/
|
||||
public void subscribe(String id) {
|
||||
this.open(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅
|
||||
*
|
||||
* @param id 终端媒体流ID
|
||||
*
|
||||
* @see #close(String)
|
||||
*/
|
||||
public void unsubscribe(String id) {
|
||||
this.close(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -20,19 +46,19 @@ public class ClientMediaSubscriber implements ClientMediaHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
public void pause(String id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
public void resume(String id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
public void close(String id) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.acgist.taoyao.signal.media.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 终端媒体流
|
||||
*
|
||||
@@ -58,23 +60,31 @@ public interface ClientMediaStream {
|
||||
|
||||
/**
|
||||
* 打开终端媒体流
|
||||
*
|
||||
* @throws IO异常
|
||||
*/
|
||||
void open();
|
||||
void open() throws IOException;
|
||||
|
||||
/**
|
||||
* 暂停终端媒体流
|
||||
*
|
||||
* @throws IO异常
|
||||
*/
|
||||
void pause();
|
||||
void pause() throws IOException;
|
||||
|
||||
/**
|
||||
* 恢复终端媒体流
|
||||
*
|
||||
* @throws IO异常
|
||||
*/
|
||||
void resume();
|
||||
void resume() throws IOException;
|
||||
|
||||
/**
|
||||
* 关闭终端媒体流
|
||||
*
|
||||
* @throws IO异常
|
||||
*/
|
||||
void close();
|
||||
void close() throws IOException;
|
||||
|
||||
/**
|
||||
* @return 终端媒体流类型
|
||||
|
||||
@@ -1,7 +1,57 @@
|
||||
# WebRTC
|
||||
|
||||
## 安装
|
||||
## WebRTC协议栈
|
||||
|
||||
## 观察者
|
||||
|协议|描述|
|
||||
|:--|:--|
|
||||
|UDP|基础协议|
|
||||
|DTLS|UDP数据包传输层安全性协议|
|
||||
|RTP|实时传输协议(音频视频)|
|
||||
|SRTP|RTP + DTLS|
|
||||
|RTCP|RTP传输控制协议(监控数据传输质量并给予数据发送方反馈)|
|
||||
|SRTCP|RTCP + DTLS|
|
||||
|SCTP|流控制传输协议(自定义的应用数据传输)|
|
||||
|STUN/TURN|内网穿透协议|
|
||||
|
||||
订阅发布
|
||||
## ICE/SIP/SDP
|
||||
|
||||
ICE信息的描述格式通常采用标准的SDP,其全称为Session Description Protocol,即会话描述协议。
|
||||
SDP只是一种信息格式的描述标准,不属于传输协议,但是可以被其他传输协议用来交换必要的信息,例如:SIP、RTSP等等。
|
||||
|
||||
## 协议关系
|
||||
|
||||
```
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| HTTPS/WSS | | SCTP | SRTP/SRTCP |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ICE/SIP/SDP +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| TLS | | DTLS |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| HTTP/WS | STUN/TURN | RTP/RTCP |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| TCP | UDP |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| IPv4/IPv6 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
```
|
||||
|
||||
## 其他协议
|
||||
|
||||
|协议|描述|
|
||||
|:--|:--|
|
||||
|HLS|基于HTTP的自适应码率流媒体传输协议|
|
||||
|RTSP|可以控制媒体(点播)|
|
||||
|RTMP|实时消息传送协议|
|
||||
|
||||
## 通道
|
||||
|
||||
|通道类型|协议|
|
||||
|:--|:--|
|
||||
|信令通道|自己实现|
|
||||
|会话通道|SIP/SDP|
|
||||
|媒体通道|RTP/RTCP/SRTP/SRTCP|
|
||||
|
||||
## WebRTC资料
|
||||
|
||||
[GB28181](https://blog.csdn.net/jisuanji111111/article/details/121634199)
|
||||
[WebRTC协议](http://www.manoner.com/post/音视频基础/WebRTC核心组件和协议栈/)
|
||||
[WebRTC开源项目](https://blog.csdn.net/ababab12345/article/details/115585378)
|
||||
|
||||
@@ -17,10 +17,18 @@
|
||||
<description>WebRTC模块</description>
|
||||
|
||||
<modules>
|
||||
<module>taoyao-webrtc-jni</module>
|
||||
<module>taoyao-webrtc-sfu</module>
|
||||
<module>taoyao-webrtc-mcu</module>
|
||||
<module>taoyao-webrtc-mesh</module>
|
||||
<module>taoyao-webrtc-jitsi</module>
|
||||
<module>taoyao-webrtc-kurento</module>
|
||||
</modules>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-boot</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
0
taoyao-webrtc/taoyao-webrtc-jitsi/README.md
Normal file
0
taoyao-webrtc/taoyao-webrtc-jitsi/README.md
Normal file
30
taoyao-webrtc/taoyao-webrtc-jitsi/pom.xml
Normal file
30
taoyao-webrtc/taoyao-webrtc-jitsi/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>taoyao-webrtc-jitsi</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>taoyao-webrtc-jitsi</name>
|
||||
<description>WebRTC协议簇实现:jitsi</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jitsi</groupId>
|
||||
<artifactId>ice4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jitsi</groupId>
|
||||
<artifactId>libjitsi</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
0
taoyao-webrtc/taoyao-webrtc-kurento/README.md
Normal file
0
taoyao-webrtc/taoyao-webrtc-kurento/README.md
Normal file
@@ -10,13 +10,17 @@
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>taoyao-webrtc-jni</artifactId>
|
||||
<artifactId>taoyao-webrtc-kurento</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>taoyao-webrtc-jni</name>
|
||||
<description>WebRTC JNI:WebRTC本地接口</description>
|
||||
<name>taoyao-webrtc-kurento</name>
|
||||
<description>WebRTC协议簇实现:kurento</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.kurento</groupId>
|
||||
<artifactId>kurento-client</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -19,7 +19,11 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-jni</artifactId>
|
||||
<artifactId>taoyao-webrtc-jitsi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-kurento</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-jni</artifactId>
|
||||
<artifactId>taoyao-webrtc-jitsi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.acgist</groupId>
|
||||
<artifactId>taoyao-webrtc-kurento</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user