package com.ruoyi.video.service; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.crypto.digest.MD5; import com.ruoyi.video.common.ClientType; import com.ruoyi.video.domain.dto.CameraDto; import com.ruoyi.video.thread.MediaTransfer; import com.ruoyi.video.thread.MediaTransferFlvByFFmpeg; import com.ruoyi.video.thread.MediaTransferFlvByJavacv; import io.netty.channel.ChannelHandlerContext; import java.util.concurrent.ConcurrentHashMap; import org.springframework.stereotype.Service; /** * 媒体服务,支持全局网络超时、读写超时、无人拉流持续时长自动关闭流等配置 * @Author: orange * @CreateTime: 2025-01-16 */ @Service public class MediaService { /** * 缓存流转换线程 */ public static ConcurrentHashMap cameras = new ConcurrentHashMap<>(); /** * http-flv播放 * @param cameraDto * @param ctx */ public void playForHttp(CameraDto cameraDto, ChannelHandlerContext ctx) { if (cameras.containsKey(cameraDto.getMediaKey())) { MediaTransfer mediaConvert = cameras.get(cameraDto.getMediaKey()); if(mediaConvert instanceof MediaTransferFlvByJavacv) { MediaTransferFlvByJavacv mediaTransferFlvByJavacv = (MediaTransferFlvByJavacv) mediaConvert; //如果当前已经用ffmpeg,则重新拉流 if(cameraDto.isEnabledFFmpeg()) { mediaTransferFlvByJavacv.setRunning(false); cameras.remove(cameraDto.getMediaKey()); this.playForHttp(cameraDto, ctx); } else { mediaTransferFlvByJavacv.addClient(ctx, ClientType.HTTP); } } else if (mediaConvert instanceof MediaTransferFlvByFFmpeg) { MediaTransferFlvByFFmpeg mediaTransferFlvByFFmpeg = (MediaTransferFlvByFFmpeg) mediaConvert; //如果当前已经用javacv,则关闭再重新拉流 if(!cameraDto.isEnabledFFmpeg()) { mediaTransferFlvByFFmpeg.stopFFmpeg(); cameras.remove(cameraDto.getMediaKey()); this.playForHttp(cameraDto, ctx); } else { mediaTransferFlvByFFmpeg.addClient(ctx, ClientType.HTTP); } } } else { if(cameraDto.isEnabledFFmpeg()) { MediaTransferFlvByFFmpeg mediaft = new MediaTransferFlvByFFmpeg(cameraDto); mediaft.execute(); cameras.put(cameraDto.getMediaKey(), mediaft); mediaft.addClient(ctx, ClientType.HTTP); } else { MediaTransferFlvByJavacv mediaConvert = new MediaTransferFlvByJavacv(cameraDto); cameras.put(cameraDto.getMediaKey(), mediaConvert); ThreadUtil.execute(mediaConvert); mediaConvert.addClient(ctx, ClientType.HTTP); } } } /** * ws-flv播放 * @param cameraDto * @param ctx */ public void playForWs(CameraDto cameraDto, ChannelHandlerContext ctx) { if (cameras.containsKey(cameraDto.getMediaKey())) { MediaTransfer mediaConvert = cameras.get(cameraDto.getMediaKey()); if(mediaConvert instanceof MediaTransferFlvByJavacv) { MediaTransferFlvByJavacv mediaTransferFlvByJavacv = (MediaTransferFlvByJavacv) mediaConvert; //如果当前已经用ffmpeg,则重新拉流 if(cameraDto.isEnabledFFmpeg()) { mediaTransferFlvByJavacv.setRunning(false); cameras.remove(cameraDto.getMediaKey()); this.playForWs(cameraDto, ctx); } else { mediaTransferFlvByJavacv.addClient(ctx, ClientType.WEBSOCKET); } } else if (mediaConvert instanceof MediaTransferFlvByFFmpeg) { MediaTransferFlvByFFmpeg mediaTransferFlvByFFmpeg = (MediaTransferFlvByFFmpeg) mediaConvert; //如果当前已经用javacv,则关闭再重新拉流 if(!cameraDto.isEnabledFFmpeg()) { mediaTransferFlvByFFmpeg.stopFFmpeg(); cameras.remove(cameraDto.getMediaKey()); this.playForWs(cameraDto, ctx); } else { mediaTransferFlvByFFmpeg.addClient(ctx, ClientType.WEBSOCKET); } } } else { if(cameraDto.isEnabledFFmpeg()) { MediaTransferFlvByFFmpeg mediaft = new MediaTransferFlvByFFmpeg(cameraDto); mediaft.execute(); cameras.put(cameraDto.getMediaKey(), mediaft); mediaft.addClient(ctx, ClientType.WEBSOCKET); } else { MediaTransferFlvByJavacv mediaConvert = new MediaTransferFlvByJavacv(cameraDto); cameras.put(cameraDto.getMediaKey(), mediaConvert); ThreadUtil.execute(mediaConvert); mediaConvert.addClient(ctx, ClientType.WEBSOCKET); } } } /** * api播放 * @param cameraDto * @return */ public boolean playForApi(CameraDto cameraDto) { // 区分不同媒体 String mediaKey = MD5.create().digestHex(cameraDto.getUrl()); cameraDto.setMediaKey(mediaKey); cameraDto.setEnabledFlv(true); MediaTransfer mediaTransfer = cameras.get(cameraDto.getMediaKey()); if (null == mediaTransfer) { if(cameraDto.isEnabledFFmpeg()) { MediaTransferFlvByFFmpeg mediaft = new MediaTransferFlvByFFmpeg(cameraDto); mediaft.execute(); cameras.put(cameraDto.getMediaKey(), mediaft); } else { MediaTransferFlvByJavacv mediaConvert = new MediaTransferFlvByJavacv(cameraDto); cameras.put(cameraDto.getMediaKey(), mediaConvert); ThreadUtil.execute(mediaConvert); } } mediaTransfer = cameras.get(cameraDto.getMediaKey()); //同步等待 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; } /** * 关闭流 * @param cameraDto */ public void closeForApi(CameraDto cameraDto) { cameraDto.setEnabledFlv(false); if (cameras.containsKey(cameraDto.getMediaKey())) { MediaTransfer mediaConvert = cameras.get(cameraDto.getMediaKey()); if(mediaConvert instanceof MediaTransferFlvByJavacv) { MediaTransferFlvByJavacv mediaTransferFlvByJavacv = (MediaTransferFlvByJavacv) mediaConvert; mediaTransferFlvByJavacv.setRunning(false); cameras.remove(cameraDto.getMediaKey()); } else if (mediaConvert instanceof MediaTransferFlvByFFmpeg) { MediaTransferFlvByFFmpeg mediaTransferFlvByFFmpeg = (MediaTransferFlvByFFmpeg) mediaConvert; mediaTransferFlvByFFmpeg.stopFFmpeg(); cameras.remove(cameraDto.getMediaKey()); } } } }