Ver Fonte

更新前端功能交互

MisterZhang há 4 anos atrás
pai
commit
65cf22ec91

+ 1 - 1
README.md

@@ -4,7 +4,7 @@
 #### 介绍
 Springboot、netty实现的http-flv、websocket-flv流媒体服务(可用于直播点播),支持rtsp、h264、h265等、rtmp等多种源,h5纯js播放(不依赖flash),不需要依赖nginx等第三方,延迟大部分在1-5秒内(已经支持转复用,h264的流自动转封装,超低延迟。PS:当然还有种更低延迟的不用flv方案没时间写了,但是主要是flv比较大众,这个一般也够用了)。
 
-******************即将有重大更新*********************
+******************即将有重大更新,前端尚未更新,教程等我抽空完善*********************
 
 #### 成品下载
 !!!此版本已修复大华等rtsp后面带参数的地址解析、并支持转复用或转码

+ 84 - 17
src/main/java/com/zj/controller/StreamController.java

@@ -1,24 +1,27 @@
 package com.zj.controller;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import com.zj.dto.Camera;
 import com.zj.service.CameraRepository;
 import com.zj.service.MediaService;
-import com.zj.thread.MediaConvert;
 import com.zj.thread.MediaTransfer;
+import com.zj.thread.MediaTransferFlvByFFmpeg;
 import com.zj.thread.MediaTransferFlvByJavacv;
+import com.zj.vo.CameraVo;
 import com.zj.vo.Result;
 
 import cn.hutool.crypto.digest.MD5;
 
 /**
- * 
+ * api管理接口
+ * 后续可能改为使用数据库
  * @author ZJ
  *
  */
@@ -30,52 +33,116 @@ public class StreamController {
 	@Autowired
 	private MediaService mediaService;
 	
+	/**
+	 * 新增流
+	 * @param camera
+	 * @return
+	 */
 	@RequestMapping("add")
 	public Result add(Camera camera) {
 		String res = cameraRepository.add(camera);
-		return new Result(res, 200, null);
+		return new Result(res, 200, true);
 	}
+	
+	/**
+	 * 编辑流
+	 * @param camera
+	 * @return
+	 */
 	@RequestMapping("edit")
 	public Result edit(Camera camera) {
 		String res = cameraRepository.edit(camera);
-		return new Result(res, 200, null);
+		return new Result(res, 200, true);
 	}
+	
+	/**
+	 * 删除流(会停止推流)
+	 * @param camera
+	 * @return
+	 */
 	@RequestMapping("del")
 	public Result del(Camera camera) {
 		mediaService.closeForApi(camera);
 		cameraRepository.del(camera);
-		return new Result("删除成功", 200, null);
+		return new Result("删除成功", 200, true);
 	}
+	
+	/**
+	 * 停止推流
+	 * @param camera
+	 * @return
+	 */
 	@RequestMapping("stop")
 	public Result stop(Camera camera) {
 		mediaService.closeForApi(camera);
-		return new Result("停止推流", 200, null);
+		return new Result("停止推流", 200, true);
 	}
 	
+	/**
+	 * 开始推流
+	 * @param camera
+	 * @return
+	 */
 	@RequestMapping("start")
 	public Result start(Camera camera) {
-		mediaService.playForApi(camera);
-		return new Result("开始推流", 200, null);
+		boolean playForApi = mediaService.playForApi(camera);
+		return new Result("开始推流", 200, playForApi);
+	}
+	
+	/**
+	 * 开启hls
+	 * @param camera
+	 * @return
+	 */
+	@RequestMapping("startHls")
+	public Result startHls(Camera camera) {
+//		boolean playForApi = mediaService.playForApi(camera);
+		return new Result("开始推流", 200, true);
+	}
+	
+	/**
+	 * 关闭hls
+	 * @param camera
+	 * @return
+	 */
+	@RequestMapping("stopHls")
+	public Result stopHls(Camera camera) {
+//		mediaService.closeForApi(camera);
+		return new Result("停止推流", 200, true);
 	}
 	
+	/**
+	 * 列表
+	 * @return
+	 */
 	@RequestMapping("list")
 	public Result list() {
 		Collection<Camera> values = CameraRepository.cameraMap.values();
+		
+		List<CameraVo> list = new ArrayList<CameraVo>();
 		for (Camera camera : values) {
 			String digestHex = MD5.create().digestHex(camera.getUrl());
+			
 			MediaTransfer mediaConvert = MediaService.cameras.get(digestHex);
+			
+			CameraVo cameraVo = new CameraVo();
+			
+			cameraVo.setUrl(camera.getUrl());
+			cameraVo.setRemark(camera.getRemark());
 			if(mediaConvert instanceof MediaTransferFlvByJavacv) {
 				MediaTransferFlvByJavacv mediaTransferFlvByJavacv = (MediaTransferFlvByJavacv) mediaConvert;
-//				MediaConvert mediaConvert = MediaService.cameras.get(digestHex);
-				if(mediaConvert != null) {
-					camera.setEnabledFlv(mediaTransferFlvByJavacv.isRunning());
-				} else {
-					camera.setEnabledFlv(false);
-				}
-			}
+				cameraVo.setEnabledFlv(mediaTransferFlvByJavacv.isRunning());
+				cameraVo.setMode("javacv");
+				
+			} if(mediaConvert instanceof MediaTransferFlvByFFmpeg) {
+				MediaTransferFlvByFFmpeg mediaTransferFlvByFFmpeg = (MediaTransferFlvByFFmpeg) mediaConvert;
+				cameraVo.setEnabledFlv(mediaTransferFlvByFFmpeg.isRunning());
+				cameraVo.setMode("ffmpeg");
+			} 
 
+			list.add(cameraVo);
 		}
-		return new Result("查询成功", 200, values);
+		return new Result("查询成功", 200, list);
 	}
 
 }

+ 8 - 1
src/main/java/com/zj/init/InitServer.java

@@ -22,6 +22,7 @@ import com.zj.server.MediaServer;
 import com.zj.service.CameraRepository;
 import com.zj.service.MediaService;
 
+import cn.hutool.crypto.digest.MD5;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONUtil;
 import lombok.extern.slf4j.Slf4j;
@@ -67,7 +68,13 @@ public class InitServer implements CommandLineRunner {
 			cameraRepository.readDataToMap(list);
 
 			for (Camera camera : CameraRepository.cameraMap.values()) {
-				mediaService.playForApi(camera);
+				// 区分不同媒体
+				String mediaKey = MD5.create().digestHex(camera.getUrl());
+				camera.setMediaKey(mediaKey);
+				//已启用的自动拉流,不启用的不自动拉
+				if(camera.isEnabledFlv()) {
+					mediaService.playForApi(camera);
+				}
 			}
 		} else {
 			log.info("未发现camera.json,您可以通过restful api添加或删除流!");

+ 2 - 0
src/main/java/com/zj/service/CameraRepository.java

@@ -44,6 +44,7 @@ public class CameraRepository {
 	 */
 	public String add(Camera camera) {
 		String digestHex = MD5.create().digestHex(camera.getUrl());
+		camera.setMediaKey(digestHex);
 
 		if (cameraMap.containsKey(digestHex)) {
 			log.info("\r\n已存在流>>>\r\n{}\r\n", camera.getUrl());
@@ -61,6 +62,7 @@ public class CameraRepository {
 
 	public String edit(Camera camera) {
 		String digestHex = MD5.create().digestHex(camera.getUrl());
+		camera.setMediaKey(digestHex);
 
 		if (cameraMap.containsKey(digestHex)) {
 			cameraMap.put(digestHex, camera);

+ 39 - 6
src/main/java/com/zj/service/MediaService.java

@@ -12,6 +12,7 @@ import com.zj.thread.MediaTransferFlvByFFmpeg;
 import com.zj.thread.MediaTransferFlvByJavacv;
 
 import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.crypto.digest.MD5;
 import io.netty.channel.ChannelHandlerContext;
 
 /**
@@ -130,11 +131,15 @@ public class MediaService {
 	/**
 	 * api播放
 	 * @param camera
+	 * @return 
 	 */
-	public void playForApi(Camera camera) {
-		doStore(camera);
+	public boolean playForApi(Camera camera) {
+		// 区分不同媒体
+		String mediaKey = MD5.create().digestHex(camera.getUrl());
+		camera.setMediaKey(mediaKey);
 		
-		if (!cameras.containsKey(camera.getMediaKey())) {
+		MediaTransfer mediaTransfer = cameras.get(camera.getMediaKey());
+		if (null == mediaTransfer) {
 			if(camera.isEnabledFFmpeg()) {
 				MediaTransferFlvByFFmpeg mediaft = new MediaTransferFlvByFFmpeg(camera);
 				mediaft.execute();
@@ -145,6 +150,34 @@ public class MediaService {
 				ThreadUtil.execute(mediaConvert);
 			}
 		}
+		
+		//同步等待
+		if(mediaTransfer instanceof MediaTransferFlvByJavacv) {
+			MediaTransferFlvByJavacv mediaft = (MediaTransferFlvByJavacv) mediaTransfer;
+			// 30秒还没true认为启动不了
+			for (int i = 0; i < 60; i++) {
+				if (mediaft.isRunning() && mediaft.isGrabberStatus() && mediaft.isRecorderStatus()) {
+					return true;
+				}
+				try {
+					Thread.sleep(500);
+				} catch (InterruptedException e) {
+				}
+			}
+		} else if (mediaTransfer instanceof MediaTransferFlvByFFmpeg) {
+			MediaTransferFlvByFFmpeg mediaft = (MediaTransferFlvByFFmpeg) mediaTransfer;
+			// 30秒还没true认为启动不了
+			for (int i = 0; i < 60; i++) {
+				if (mediaft.isRunning()) {
+					return true;
+				}
+				try {
+					Thread.sleep(500);
+				} catch (InterruptedException e) {
+				}
+			}
+		}
+		return false;
 	}
 	
 	/**
@@ -152,6 +185,8 @@ public class MediaService {
 	 * @param camera
 	 */
 	public void closeForApi(Camera camera) {
+		String mediaKey = MD5.create().digestHex(camera.getUrl());
+		camera.setMediaKey(mediaKey);
 		
 		if (cameras.containsKey(camera.getMediaKey())) {
 			MediaTransfer mediaConvert = cameras.get(camera.getMediaKey());
@@ -173,9 +208,7 @@ public class MediaService {
 		 */
 		if (!camera.isAutoClose()) {
 			cameraRepository.add(camera);
-		} else {
-			cameraRepository.del(camera);
-		}
+		} 
 	}
 	
 }

+ 16 - 0
src/main/java/com/zj/thread/MediaTransferFlvByJavacv.java

@@ -115,6 +115,22 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable
 		this.running = running;
 	}
 
+	public boolean isGrabberStatus() {
+		return grabberStatus;
+	}
+
+	public void setGrabberStatus(boolean grabberStatus) {
+		this.grabberStatus = grabberStatus;
+	}
+
+	public boolean isRecorderStatus() {
+		return recorderStatus;
+	}
+
+	public void setRecorderStatus(boolean recorderStatus) {
+		this.recorderStatus = recorderStatus;
+	}
+
 	/**
 	 * 创建拉流器
 	 * @return

+ 19 - 0
src/main/java/com/zj/vo/CameraVo.java

@@ -0,0 +1,19 @@
+package com.zj.vo;
+
+import lombok.Data;
+
+/**
+ * 
+ * @author ZJ
+ *
+ */
+@Data
+public class CameraVo {
+
+	private String url;
+	private String remark;
+	private boolean enabledFlv = false;
+	private boolean enabledHls = false;
+	
+	private String mode = "未开启";
+}