[*] 服务端录制
This commit is contained in:
@@ -4,11 +4,11 @@ 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.FfmpegProperties;
|
||||
import com.acgist.taoyao.boot.config.MediaProperties;
|
||||
import com.acgist.taoyao.boot.config.SocketProperties;
|
||||
import com.acgist.taoyao.boot.config.WebrtcProperties;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.config.camera.CameraProperties;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
@@ -29,7 +29,7 @@ import lombok.RequiredArgsConstructor;
|
||||
public class ConfigController {
|
||||
|
||||
private final MediaProperties mediaProperties;
|
||||
private final CameraProperties cameraProperties;
|
||||
private final FfmpegProperties ffmpegProperties;
|
||||
private final SocketProperties socketProperties;
|
||||
private final WebrtcProperties webrtcProperties;
|
||||
|
||||
@@ -40,11 +40,11 @@ public class ConfigController {
|
||||
return Message.success(this.mediaProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "摄像头配置", description = "摄像头配置")
|
||||
@GetMapping("/camera")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = CameraProperties.class)))
|
||||
private Message camera() {
|
||||
return Message.success(this.cameraProperties);
|
||||
@Operation(summary = "FFmpeg配置", description = "FFmpeg配置")
|
||||
@GetMapping("/ffmpeg")
|
||||
@ApiResponse(content = @Content(schema = @Schema(implementation = FfmpegProperties.class)))
|
||||
public Message ffmpeg() {
|
||||
return Message.success(this.ffmpegProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "Socket配置", description = "Socket配置")
|
||||
|
||||
@@ -9,19 +9,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import com.acgist.taoyao.boot.config.MediaAudioProperties;
|
||||
import com.acgist.taoyao.boot.config.MediaVideoProperties;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.config.camera.AiProperties;
|
||||
import com.acgist.taoyao.signal.config.camera.BeautyProperties;
|
||||
import com.acgist.taoyao.signal.config.camera.WatermarkProperties;
|
||||
import com.acgist.taoyao.signal.model.control.PtzModel;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlAiProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlBeautyProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlBellProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlConfigAudioProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlConfigVideoProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlPhotographProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlPtzProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlRecordProtocol;
|
||||
import com.acgist.taoyao.signal.protocol.control.ControlWatermarkProtocol;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -41,68 +33,40 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public class ControlController {
|
||||
|
||||
private final ControlAiProtocol controlAiProtocol;
|
||||
private final ControlPtzProtocol controlPtzProtocol;
|
||||
private final ControlBellProtocol controlBellProtocol;
|
||||
private final ControlBeautyProtocol controlBeautyProtocol;
|
||||
private final ControlRecordProtocol controlRecordProtocol;
|
||||
private final ControlWatermarkProtocol controlWatermarkProtocol;
|
||||
private final ControlPhotographProtocol controlPhotographProtocol;
|
||||
private final ControlConfigAudioProtocol controlConfigAudioProtocol;
|
||||
private final ControlConfigVideoProtocol controlConfigVideoProtocol;
|
||||
|
||||
@Operation(summary = "AI识别", description = "AI识别控制")
|
||||
@GetMapping("/ai/{clientId}")
|
||||
public Message ai(@PathVariable String clientId, @Valid AiProperties aiProperties) {
|
||||
return Message.success(this.controlAiProtocol.execute(clientId, aiProperties));
|
||||
}
|
||||
|
||||
@Operation(summary = "PTZ", description = "PTZ控制")
|
||||
@GetMapping("/ptz/{clientId}")
|
||||
public Message ptz(@PathVariable String clientId, @Valid PtzModel ptzModel) {
|
||||
return Message.success(this.controlPtzProtocol.execute(clientId, ptzModel));
|
||||
}
|
||||
|
||||
@Operation(summary = "响铃", description = "响铃控制")
|
||||
@GetMapping("/bell/{clientId}")
|
||||
public Message bell(@PathVariable String clientId, @NotNull(message = "没有指定操作状态") Boolean enabled) {
|
||||
return Message.success(this.controlBellProtocol.execute(clientId, enabled));
|
||||
}
|
||||
|
||||
@Operation(summary = "美颜", description = "美颜控制")
|
||||
@GetMapping("/beauty/{clientId}")
|
||||
public Message beauty(@PathVariable String clientId, @Valid BeautyProperties beautyProperties) {
|
||||
return Message.success(this.controlBeautyProtocol.execute(clientId, beautyProperties));
|
||||
return this.controlBellProtocol.execute(clientId, enabled);
|
||||
}
|
||||
|
||||
@Operation(summary = "录像", description = "录像控制")
|
||||
@GetMapping("/record/{clientId}")
|
||||
public Message record(@PathVariable String clientId, @NotNull(message = "没有指定操作状态") Boolean enabled) {
|
||||
return Message.success(this.controlRecordProtocol.execute(clientId, enabled));
|
||||
}
|
||||
|
||||
@Operation(summary = "水印", description = "水印控制")
|
||||
@GetMapping("/watermark/{clientId}")
|
||||
public Message watermark(@PathVariable String clientId, @Valid WatermarkProperties watermarkProperties) {
|
||||
return Message.success(this.controlWatermarkProtocol.execute(clientId, watermarkProperties));
|
||||
return this.controlRecordProtocol.execute(clientId, enabled);
|
||||
}
|
||||
|
||||
@Operation(summary = "拍照", description = "拍照控制")
|
||||
@GetMapping("/photograph/{clientId}")
|
||||
public Message photograph(@PathVariable String clientId) {
|
||||
return Message.success(this.controlPhotographProtocol.execute(clientId));
|
||||
return this.controlPhotographProtocol.execute(clientId);
|
||||
}
|
||||
|
||||
@Operation(summary = "配置音频", description = "配置音频")
|
||||
@GetMapping("/config/audio/{clientId}")
|
||||
public Message configAudio(@PathVariable String clientId, @Valid MediaAudioProperties mediaAudioProperties) {
|
||||
return Message.success(this.controlConfigAudioProtocol.execute(clientId, mediaAudioProperties));
|
||||
return this.controlConfigAudioProtocol.execute(clientId, mediaAudioProperties);
|
||||
}
|
||||
|
||||
@Operation(summary = "配置视频", description = "配置视频")
|
||||
@GetMapping("/config/video/{clientId}")
|
||||
public Message configVideo(@PathVariable String clientId, @Valid MediaVideoProperties mediaVideoProperties) {
|
||||
return Message.success(this.controlConfigVideoProtocol.execute(clientId, mediaVideoProperties));
|
||||
return this.controlConfigVideoProtocol.execute(clientId, mediaVideoProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.acgist.taoyao.controller;
|
||||
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.signal.protocol.media.MediaRecordProtocol;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 媒体
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@Tag(name = "媒体", description = "媒体管理")
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/media")
|
||||
@RequiredArgsConstructor
|
||||
public class MediaController {
|
||||
|
||||
private final MediaRecordProtocol mediaRecordProtocol;
|
||||
|
||||
@Operation(summary = "录像", description = "媒体录像")
|
||||
@GetMapping("/record/{roomId}/{clientId}")
|
||||
public Message record(@PathVariable String roomId, @PathVariable String clientId, @NotNull(message = "没有指定操作状态") Boolean enabled) {
|
||||
return this.mediaRecordProtocol.execute(roomId, clientId, enabled);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -67,7 +67,7 @@ taoyao:
|
||||
max-client-index: 99999
|
||||
# 媒体配置
|
||||
media:
|
||||
# =============== 视频配置 ===============
|
||||
# 视频配置
|
||||
# 宽度
|
||||
min-width: 720
|
||||
max-width: 4096
|
||||
@@ -80,7 +80,7 @@ taoyao:
|
||||
# 视频码率
|
||||
min-video-bitrate: 800
|
||||
max-video-bitrate: 1600
|
||||
# =============== 音频配置 ===============
|
||||
# 音频配置
|
||||
# 采样位数
|
||||
min-sample-size: 8
|
||||
max-sample-size: 32
|
||||
@@ -196,35 +196,6 @@ taoyao:
|
||||
# port: 3478
|
||||
# username: taoyao
|
||||
# password: taoyao
|
||||
# 摄像头配置
|
||||
camera:
|
||||
# 混音
|
||||
audio-mixer: false
|
||||
# 变声
|
||||
audio-changer: false
|
||||
# 降噪
|
||||
audio-denoise: true
|
||||
# 存储目录
|
||||
storage-path: /data/taoyao/storage
|
||||
# 图片存储目录
|
||||
storage-image-path: /data/taoyao/storage/image
|
||||
# 视频存储目录
|
||||
storage-video-path: /data/taoyao/storage/video
|
||||
# AI识别
|
||||
ai:
|
||||
enabled: false
|
||||
type: PERSON
|
||||
# 美颜
|
||||
beauty:
|
||||
enabled: false
|
||||
level: 10
|
||||
# 水印
|
||||
watermark:
|
||||
enabled: false
|
||||
text: taoyao
|
||||
posx: 10
|
||||
posy: 10
|
||||
opacity: 0.8
|
||||
# 安全配置
|
||||
security:
|
||||
enabled: true
|
||||
@@ -253,6 +224,36 @@ taoyao:
|
||||
- network: 192.168.8.0
|
||||
inner-host:
|
||||
outer-host:
|
||||
ffmpeg:
|
||||
# SDP:VP8 | H264
|
||||
sdp: |
|
||||
v=0
|
||||
o=- 0 0 IN IP4 127.0.0.1
|
||||
s=TaoyaoRecord
|
||||
t=0 0
|
||||
m=audio %d RTP/AVP 97
|
||||
c=IN IP4 127.0.0.1
|
||||
a=rtpmap:97 opus/48000/2
|
||||
a=fmtp:97 sprop-stereo=1
|
||||
m=video %d RTP/AVP 96
|
||||
c=IN IP4 127.0.0.1
|
||||
a=rtpmap:96 H264/90000
|
||||
a=fmtp:96 packetization-mode=1
|
||||
# 录像命令
|
||||
record: ffmpeg -y -protocol_whitelist "file,rtp,udp" -i %s %s
|
||||
# 截图命令
|
||||
preview: ffmpeg -y -i %s -t %d -f image2 %s
|
||||
# 时长命令
|
||||
duration: ffprobe -i %s -show_entries format=duration
|
||||
# 存储目录
|
||||
storage-path: /data/taoyao/storage
|
||||
# 图片存储目录
|
||||
storage-image-path: /data/taoyao/storage/image
|
||||
# 视频存储目录
|
||||
storage-video-path: /data/taoyao/storage/video
|
||||
# 端口范围
|
||||
min-port: 50000
|
||||
max-port: 59999
|
||||
# 脚本配置
|
||||
script:
|
||||
enabled: true
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.acgist.taoyao;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.acgist.taoyao.boot.config.FfmpegProperties;
|
||||
import com.acgist.taoyao.signal.party.media.Recorder;
|
||||
|
||||
public class RecorderTest {
|
||||
|
||||
@Test
|
||||
public void testStart() throws InterruptedException {
|
||||
final FfmpegProperties ffmpegProperties = new FfmpegProperties();
|
||||
ffmpegProperties.setStorageVideoPath("D:\\tmp\\video");
|
||||
ffmpegProperties.setSdp("""
|
||||
v=0
|
||||
o=- 0 0 IN IP4 127.0.0.1
|
||||
s=TaoyaoRecord
|
||||
t=0 0
|
||||
m=audio %d RTP/AVP 97
|
||||
c=IN IP4 127.0.0.1
|
||||
a=rtpmap:97 opus/48000/2
|
||||
a=fmtp:97 sprop-stereo=1
|
||||
m=video %d RTP/AVP 96
|
||||
c=IN IP4 127.0.0.1
|
||||
a=rtpmap:96 H264/90000
|
||||
a=fmtp:96 packetization-mode=1
|
||||
""");
|
||||
ffmpegProperties.setRecord("ffmpeg -y -protocol_whitelist \"file,rtp,udp\" -i %s %s");
|
||||
final Recorder recorder = new Recorder(ffmpegProperties);
|
||||
recorder.start();
|
||||
Thread.sleep(20 * 1000);
|
||||
recorder.stop();
|
||||
Thread.sleep(20 * 1000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,20 +82,13 @@ public class RtpTest {
|
||||
// {"header":{"v":"1.0.0","id":1215310510002009,"signal":"room::enter"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9"}}
|
||||
// {"header":{"v":"1.0.0","id":1215310510002010,"signal":"media::transport::plain"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","rtcpMux":false,"comedia":true}}
|
||||
// {"header":{"v":"1.0.0","id":1215375110006012,"signal":"media::produce"},"body":{"kind":"video","roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","transportId":"14dc9307-bf9c-4442-a9ad-ce6a97623ef4","appData":{},"rtpParameters":{"codecs":[{"mimeType":"video/vp8","clockRate":90000,"payloadType":102,"rtcpFeedback":[]}],"encodings":[{"ssrc":123123}]}}}
|
||||
// ffmpeg直接播放
|
||||
// ffmpeg -re -i video.mp4 -c:a copy -vn -map 0:1 -f rtp rtp://localhost:6666 > taoyao.sdp
|
||||
// ffmpeg -re -i video.mp4 -c:v copy -an -map 0:0 -f rtp rtp://localhost:6666 > taoyao.sdp
|
||||
// ffplay -protocol_whitelist "file,udp,rtp" taoyao.sdp
|
||||
// ffmpeg不支持rtcpMux
|
||||
// ffmpeg -re -i video.mp4 -c:v libvpx -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// ffmpeg -re -i video.mp4 -c:v vp8 -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// ffmpeg -re -i video.mp4 -c:v libvpx -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||
// 音频视频同时传输
|
||||
// ffmpeg -re -i video.mp4 -c:v copy -an -f rtp rtp://192.168.8.122:6666 -c:a copy -vn -f rtp rtp://192.168.8.122:7777 -sdp_file taoyao.sdp
|
||||
// ffmpeg -re -i video.mp4 -c:a libopus -vn -f rtp rtp://192.168.1.110:8888 -c:v libx264 -an -f rtp rtp://192.168.1.110:9999 -sdp_file taoyao.sdp
|
||||
// ffplay -protocol_whitelist "file,rtp,udp" -i taoyao.sdp
|
||||
// ffmpeg -protocol_whitelist "file,rtp,udp" -i taoyao.sdp taoyao.mp4
|
||||
// 混音
|
||||
// ffmpeg -re -i video.mp4 -i music.mp3 -map 0 -vn -c:a copy -f rtp rtp://192.168.8.122:6666 -map 0 -an -c:v copy -f rtp rtp://192.168.8.122:7777 -map 1 -c:a aac -f rtp rtp://192.168.8.122:8888 -sdp_file taoyao.sdp
|
||||
// ffmpeg -protocol_whitelist "file,rtp,udp" -i taoyao.sdp -filter_complex "[0:0]aresample=8000[a0];[0:2]aresample=8000[a2];[a0][a2]amix=inputs=2" taoyao.mp4
|
||||
final Scanner scanner = new Scanner(System.in);
|
||||
do {
|
||||
if(StringUtils.isEmpty(line)) {
|
||||
|
||||
Reference in New Issue
Block a user