liusuyi
2026-06-02 f652169cc5d6501511eece5df57b7b396fd7a7ab
ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java
@@ -10,8 +10,10 @@
import com.ard.gb28181.api.RemoteGb28181Service;
import com.ard.gb28181.api.domain.Device;
import com.ard.gb28181.api.domain.DeviceChannel;
import com.ard.qs.api.RemoteQsDeviceService;
import com.ard.qs.api.domain.QsDevice;
import com.ard.gb28181.api.domain.GbChannelDTO;
import com.ard.zlm.domain.StreamChannel;
import com.ard.zlm.service.ZlmStreamService;
import com.ard.work.api.RemoteCameraService;
import com.ard.zlm.api.domain.*;
import com.ard.zlm.common.InviteErrorCode;
import com.ard.zlm.common.InviteSessionType;
@@ -22,6 +24,8 @@
import com.ard.zlm.service.IInviteStreamService;
import com.ard.zlm.service.IMediaServerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
@@ -64,7 +68,7 @@
    private RemoteGb28181Service remoteGb28181Service;
    @Resource
    private RemoteQsDeviceService remoteQsDeviceService;
    private ZlmStreamService zlmStreamService;
    @Resource
    @Lazy
@@ -489,6 +493,7 @@
     * @param id      设备id
     * @return
     */
    @Operation(summary = "gb28181播放")
    @GetMapping("/startGb28181Play/{id}")
    public DeferredResult<R<StreamContent>> startGb28181Play(
            HttpServletRequest request,
@@ -497,50 +502,50 @@
        log.info("[gb28181 开始点播] id:{} ", id);
        Assert.notNull(id, "设备id");
        R<QsDevice> qsDevicer = remoteQsDeviceService.getQsDeviceInfo(id, SecurityConstants.INNER);
        R<StreamChannel> qsDevicer = zlmStreamService.getQsDeviceInfo(id, SecurityConstants.INNER);
        if (qsDevicer.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("获取设备信息失败 id:" + id);
        }
        Assert.notNull(qsDevicer.getData(), "设备不存在 id:" + id);
        QsDevice qsDevice = qsDevicer.getData();
        StreamChannel streamChannel = qsDevicer.getData();
        if ("OFFLINE".equals(qsDevice.getDeviceStatus())) {
        if (!Boolean.TRUE.equals(streamChannel.getDeviceOnLine())) {
            throw new RuntimeException("设备不在线 id:" + id);
        }
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(qsDevice.getGbDeviceId(), SecurityConstants.INNER);
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(streamChannel.getGbDeviceId(), SecurityConstants.INNER);
        if (deviceR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备信息失败 id:" + qsDevice.getGbDeviceId());
            throw new RuntimeException("gb28181 获取设备信息失败 id:" + streamChannel.getGbDeviceId());
        }
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 id:" + qsDevice.getGbDeviceId());
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 id:" + streamChannel.getGbDeviceId());
        if (!deviceR.getData().isOnLine()) {
            throw new RuntimeException("gb28181 国标设备不在线失败 id:" + qsDevice.getGbDeviceId());
            throw new RuntimeException("gb28181 国标设备不在线失败 id:" + streamChannel.getGbDeviceId());
        }
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(qsDevice.getGbDeviceId(), qsDevice.getGbChannelId(), SecurityConstants.INNER);
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(streamChannel.getGbDeviceId(), streamChannel.getGbChannelId(), SecurityConstants.INNER);
        if (deviceChannelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + qsDevice.getGbDeviceId() + ",gbChannelId:" + qsDevice.getGbChannelId());
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
        }
        Assert.notNull(deviceChannelR.getData(), "gb28181 获取设备通道失败不存在 gbDeviceId:" + qsDevice.getGbDeviceId() + ",gbChannelId:" + qsDevice.getGbChannelId());
        Assert.notNull(deviceChannelR.getData(), "gb28181 获取设备通道失败不存在 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
        if (!"ON".equals(deviceChannelR.getData().getStatus())) {
            throw new RuntimeException("gb28181 国标设备通道不在线失败 gbDeviceId:" + qsDevice.getGbDeviceId() + ",gbChannelId:" + qsDevice.getGbChannelId());
            throw new RuntimeException("gb28181 国标设备通道不在线失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
        }
        DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
        result.onTimeout(() -> {
            log.info("[点播等待超时] gbDeviceId:{}, gbChannelId:{}, ", qsDevice.getGbDeviceId(), qsDevice.getGbChannelId());
            log.info("[点播等待超时] gbDeviceId:{}, gbChannelId:{}, ", streamChannel.getGbDeviceId(), streamChannel.getGbChannelId());
            // 释放rtpserver
            R<StreamContent> wvpResult = R.fail();
            wvpResult.setMsg("点播超时");
            result.setResult(wvpResult);
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, qsDevice.getId());
            mediaServerService.stopGb28181Play(InviteSessionType.PLAY, qsDevice, deviceR.getData(), qsDevice.getDeviceCode());
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, streamChannel.getId());
            mediaServerService.stopGb28181Play(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
        });
        ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
@@ -573,8 +578,8 @@
            }
        };
        qsDevice.setStreamMode(deviceR.getData().getStreamMode());
        mediaServerService.startGb28181Play(qsDevice, deviceR.getData(), callback);
        streamChannel.setStreamMode(deviceR.getData().getStreamMode());
        mediaServerService.startGb28181Play(streamChannel, deviceR.getData(), callback);
        return result;
    }
@@ -590,44 +595,397 @@
        log.info("[gb28181 停止点播] id:{} ", id);
        Assert.notNull(id, "设备id");
        R<QsDevice> qsDevicer = remoteQsDeviceService.getQsDeviceInfo(id, SecurityConstants.INNER);
        R<StreamChannel> qsDevicer = zlmStreamService.getQsDeviceInfo(id, SecurityConstants.INNER);
        if (qsDevicer.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("获取设备信息失败 id:" + id);
        }
        Assert.notNull(qsDevicer.getData(), "设备不存在 id:" + id);
        QsDevice qsDevice = qsDevicer.getData();
        StreamChannel streamChannel = qsDevicer.getData();
        if ("OFFLINE".equals(qsDevice.getDeviceStatus())) {
        if (!Boolean.TRUE.equals(streamChannel.getDeviceOnLine())) {
            throw new RuntimeException("设备不在线 id:" + id);
        }
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(qsDevice.getGbDeviceId(), SecurityConstants.INNER);
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(streamChannel.getGbDeviceId(), SecurityConstants.INNER);
        if (deviceR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备信息失败 id:" + qsDevice.getGbDeviceId());
            throw new RuntimeException("gb28181 获取设备信息失败 id:" + streamChannel.getGbDeviceId());
        }
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 id:" + qsDevice.getGbDeviceId());
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 id:" + streamChannel.getGbDeviceId());
        if (!deviceR.getData().isOnLine()) {
            throw new RuntimeException("gb28181 国标设备不在线失败 id:" + qsDevice.getGbDeviceId());
            throw new RuntimeException("gb28181 国标设备不在线失败 id:" + streamChannel.getGbDeviceId());
        }
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(qsDevice.getGbDeviceId(), qsDevice.getGbChannelId(), SecurityConstants.INNER);
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(streamChannel.getGbDeviceId(), streamChannel.getGbChannelId(), SecurityConstants.INNER);
        if (deviceChannelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + qsDevice.getGbDeviceId() + ",gbChannelId:" + qsDevice.getGbChannelId());
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
        }
        Assert.notNull(deviceChannelR.getData(), "gb28181 获取设备通道失败不存在 gbDeviceId:" + qsDevice.getGbDeviceId() + ",gbChannelId:" + qsDevice.getGbChannelId());
        Assert.notNull(deviceChannelR.getData(), "gb28181 获取设备通道失败不存在 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
        if (!"ON".equals(deviceChannelR.getData().getStatus())) {
            throw new RuntimeException("gb28181 国标设备通道不在线失败 gbDeviceId:" + qsDevice.getGbDeviceId() + ",gbChannelId:" + qsDevice.getGbChannelId());
            throw new RuntimeException("gb28181 国标设备通道不在线失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
        }
        mediaServerService.stopGb28181Play(InviteSessionType.PLAY, qsDevice, deviceR.getData(), qsDevice.getDeviceCode());
        mediaServerService.stopGb28181Play(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
        JSONObject json = new JSONObject();
        json.put("deviceId", qsDevice.getGbDeviceId());
        json.put("channelId", qsDevice.getGbChannelId());
        json.put("deviceId", streamChannel.getGbDeviceId());
        json.put("channelId", streamChannel.getGbChannelId());
        return AjaxResult.success(json);
    }
    /**
     * gb28181 播放(基于GbDevice,不依赖QS,按国标编码播放)
     *
     * @param request
     * @param gbDeviceId  国标设备编码
     * @param gbChannelId 国标通道编码
     * @return
     */
    @Operation(summary = "gb28181播放(按国标编码)")
    @GetMapping("/startGb28181PlayByGbDeviceId/{gbDeviceId}/{gbChannelId}")
    public DeferredResult<R<StreamContent>> startGb28181PlayByGbDeviceId(
            HttpServletRequest request,
            @PathVariable String gbDeviceId,
            @PathVariable String gbChannelId
    ) {
        log.info("[gb28181 开始点播] gbDeviceId:{}, gbChannelId:{}", gbDeviceId, gbChannelId);
        Assert.hasText(gbDeviceId, "国标设备编码不可为空");
        Assert.hasText(gbChannelId, "国标通道编码不可为空");
        // 1. 查询 GbChannel 配置
        R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannel(gbDeviceId, gbChannelId, SecurityConstants.INNER);
        if (channelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("获取GbChannel配置失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId
                    + "。设备注册后会自动创建,请确认设备已上线并完成目录同步。");
        }
        Assert.notNull(channelR.getData(), "GbChannel不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
        // 2. 查询国标设备
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
        if (deviceR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
        }
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
        if (!deviceR.getData().isOnLine()) {
            throw new RuntimeException("gb28181 国标设备不在线 deviceId:" + gbDeviceId);
        }
        // 3. 查询国标通道
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
        if (deviceChannelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        if (!"ON".equals(deviceChannelR.getData().getStatus())) {
            throw new RuntimeException("gb28181 国标设备通道不在线 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
        result.onTimeout(() -> {
            log.info("[点播等待超时] gbDeviceId:{}, gbChannelId:{}", gbDeviceId, gbChannelId);
            R<StreamContent> fail = R.fail();
            fail.setMsg("点播超时");
            result.setResult(fail);
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, streamChannel.getId());
            mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
        });
        ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                R<StreamContent> r = R.ok();
                if (streamInfo != null) {
                    if (userSetting.getUseSourceIpAsStreamIp()) {
                        streamInfo = streamInfo.clone();
                        String host;
                        try {
                            URL url = new URL(request.getRequestURL().toString());
                            host = url.getHost();
                        } catch (MalformedURLException e) {
                            host = request.getLocalAddr();
                        }
                        streamInfo.changeStreamIp(host);
                    }
                    if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
                            && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
                        streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
                    }
                    r.setData(new StreamContent(streamInfo));
                } else {
                    r.setCode(code);
                    r.setMsg(msg);
                }
                result.setResult(r);
            } else {
                result.setResult(R.fail(code, msg));
            }
        };
        mediaServerService.startGb28181PlayByGbChannel(streamChannel, deviceR.getData(), callback);
        return result;
    }
    /**
     * gb28181 停止点播(基于GbDevice,不依赖QS,按国标编码停止)
     *
     * @param gbDeviceId  国标设备编码
     * @param gbChannelId 国标通道编码
     * @return
     */
    @Operation(summary = "gb28181停止点播(按国标编码)")
    @GetMapping("/stopGb28181PlayByGbDeviceId/{gbDeviceId}/{gbChannelId}")
    public AjaxResult stopGb28181PlayByGbDeviceId(
            @PathVariable String gbDeviceId,
            @PathVariable String gbChannelId
    ) {
        log.info("[gb28181 停止点播] gbDeviceId:{}, gbChannelId:{}", gbDeviceId, gbChannelId);
        Assert.hasText(gbDeviceId, "国标设备编码不可为空");
        Assert.hasText(gbChannelId, "国标通道编码不可为空");
        // 1. 查询 GbChannel 配置
        R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannel(gbDeviceId, gbChannelId, SecurityConstants.INNER);
        if (channelR.getCode() != Constants.SUCCESS || channelR.getData() == null) {
            throw new RuntimeException("GbChannel不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
        // 2. 查询国标设备
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
        if (deviceR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
        }
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
        // 3. 查询国标通道
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
        if (deviceChannelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel,
                deviceR.getData(), streamChannel.getDeviceCode());
        JSONObject json = new JSONObject();
        json.put("gbDeviceId", gbDeviceId);
        json.put("gbChannelId", gbChannelId);
        return AjaxResult.success(json);
    }
    /**
     * gb28181 播放(基于GbChannel主键ID)
     *
     * @param request
     * @param channelId GbChannel主键ID
     * @return
     */
    @Operation(summary = "gb28181播放(按通道主键ID)")
    @GetMapping("/startGb28181PlayByChannelId/{channelId}")
    public DeferredResult<R<StreamContent>> startGb28181PlayByChannelId(
            HttpServletRequest request,
            @PathVariable Long channelId
    ) {
        log.info("[gb28181 开始点播] channelId:{}", channelId);
        Assert.notNull(channelId, "通道ID不可为空");
        // 1. 根据主键ID查询 GbChannel
        R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannelById(channelId, SecurityConstants.INNER);
        if (channelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("获取GbChannel失败 channelId:" + channelId);
        }
        Assert.notNull(channelR.getData(), "GbChannel不存在 channelId:" + channelId);
        StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
        String gbDeviceId = streamChannel.getGbDeviceId();
        String gbChannelId = streamChannel.getGbChannelId();
        // 2. 查询国标设备
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
        if (deviceR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
        }
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
        if (!deviceR.getData().isOnLine()) {
            throw new RuntimeException("gb28181 国标设备不在线 deviceId:" + gbDeviceId);
        }
        // 3. 查询国标通道
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
        if (deviceChannelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        if (!"ON".equals(deviceChannelR.getData().getStatus())) {
            throw new RuntimeException("gb28181 国标设备通道不在线 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
        result.onTimeout(() -> {
            log.info("[点播等待超时] channelId:{}, gbDeviceId:{}, gbChannelId:{}", channelId, gbDeviceId, gbChannelId);
            R<StreamContent> fail = R.fail();
            fail.setMsg("点播超时");
            result.setResult(fail);
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, streamChannel.getId());
            mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
        });
        ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                R<StreamContent> r = R.ok();
                if (streamInfo != null) {
                    if (userSetting.getUseSourceIpAsStreamIp()) {
                        streamInfo = streamInfo.clone();
                        String host;
                        try {
                            URL url = new URL(request.getRequestURL().toString());
                            host = url.getHost();
                        } catch (MalformedURLException e) {
                            host = request.getLocalAddr();
                        }
                        streamInfo.changeStreamIp(host);
                    }
                    if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
                            && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
                        streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
                    }
                    r.setData(new StreamContent(streamInfo));
                } else {
                    r.setCode(code);
                    r.setMsg(msg);
                }
                result.setResult(r);
            } else {
                result.setResult(R.fail(code, msg));
            }
        };
        mediaServerService.startGb28181PlayByGbChannel(streamChannel, deviceR.getData(), callback);
        return result;
    }
    /**
     * gb28181 停止点播(基于GbChannel主键ID)
     *
     * @param channelId GbChannel主键ID
     * @return
     */
    @Operation(summary = "gb28181停止点播(按通道主键ID)")
    @GetMapping("/stopGb28181PlayByChannelId/{channelId}")
    public AjaxResult stopGb28181PlayByChannelId(@PathVariable Long channelId) {
        log.info("[gb28181 停止点播] channelId:{}", channelId);
        Assert.notNull(channelId, "通道ID不可为空");
        // 1. 根据主键ID查询 GbChannel
        R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannelById(channelId, SecurityConstants.INNER);
        if (channelR.getCode() != Constants.SUCCESS || channelR.getData() == null) {
            throw new RuntimeException("GbChannel不存在 channelId:" + channelId);
        }
        StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
        String gbDeviceId = streamChannel.getGbDeviceId();
        String gbChannelId = streamChannel.getGbChannelId();
        // 2. 查询国标设备
        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
        if (deviceR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
        }
        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
        // 3. 查询国标通道
        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
        if (deviceChannelR.getCode() != Constants.SUCCESS) {
            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        }
        Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
        mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel,
                deviceR.getData(), streamChannel.getDeviceCode());
        JSONObject json = new JSONObject();
        json.put("channelId", channelId);
        json.put("gbDeviceId", gbDeviceId);
        json.put("gbChannelId", gbChannelId);
        return AjaxResult.success(json);
    }
    /**
     * 拉流代理(通过通道ID,简化传参)
     *
     * @param request   HttpServletRequest
     * @param channelId 通道ID
     * @return
     */
    @Operation(summary = "拉流代理(通过通道ID,简化传参)")
    @PostMapping("/streamPullPlayByChannelId/{channelId}")
    public DeferredResult<R<StreamContent>> streamPullPlayByChannelId(
            HttpServletRequest request,
            @PathVariable String channelId
    ) {
        log.info("[拉流代理-通道ID] channelId:{}", channelId);
        Assert.hasText(channelId, "通道ID不可为空");
        DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
        ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                R<StreamContent> r = R.ok();
                if (streamInfo != null) {
                    if (userSetting.getUseSourceIpAsStreamIp()) {
                        streamInfo = streamInfo.clone();
                        String host;
                        try {
                            URL url = new URL(request.getRequestURL().toString());
                            host = url.getHost();
                        } catch (MalformedURLException e) {
                            host = request.getLocalAddr();
                        }
                        streamInfo.changeStreamIp(host);
                    }
                    if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
                            && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
                        streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
                    }
                    r.setData(new StreamContent(streamInfo));
                } else {
                    r.setCode(code);
                    r.setMsg(msg);
                }
                result.setResult(r);
            } else {
                result.setResult(R.fail(code, msg));
            }
        };
        mediaServerService.streamPullPlayByChannelId(channelId, callback);
        return result;
    }
    /**
     * 停止拉流代理(通过通道ID)
     *
     * @param channelId 通道ID
     * @return
     */
    @Operation(summary = "停止拉流代理(通过通道ID)")
    @PostMapping("/stopStreamPullPlayByChannelId/{channelId}")
    public AjaxResult stopStreamPullPlayByChannelId(@PathVariable String channelId) {
        log.info("[停止拉流代理-通道ID] channelId:{}", channelId);
        Assert.hasText(channelId, "通道ID不可为空");
        mediaServerService.stopStreamPullPlayByChannelId(channelId);
        return AjaxResult.success();
    }
}