多线程摄像头业务

目录

 

1. 摄像头任务的线程池

2. 摄像头业务

3. contronller层对摄像头的增删改查

4.  实体类

5. 常量

 


1. 摄像头任务的线程池

/**
 * 线程池
 *
 * @author xuyihao
 * @date 2020/12/22
 */

@Configuration
@Slf4j
@EnableAsync
public class ExecutorConfig {

    // 只希望最多有4个摄像头线程 做任务 如果有多余的 摄像头则抛弃任务
    @Bean("asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(1);
        //配置最大线程数
        executor.setMaxPoolSize(4);
        //配置队列大小
        executor.setQueueCapacity(0);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("camera-");
        executor.setAllowCoreThreadTimeOut(true);
        executor.setKeepAliveSeconds(1);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

 

2. 摄像头业务

@Slf4j
@Component
public class CameraTask {
    @Async("asyncServiceExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void startCameraThread(CameraInfo cameraInfo) {
        // 设置线程名称 camer_+"摄像头别名"
        Thread.currentThread().setName("camera_" + cameraInfo.getAlias() + "");
        log.info(Thread.currentThread().getName() + "启动准备拉流....");
        FFmpegFrameGrabber grabber = MediaUtils.createGrabber(cameraInfo.appendUrl());
        try {
            grabber.start();
            // Constant.map.get(cameraInfo.getAlias()) 控制功能:当为true表示持续拉流获取图片  false结束这个任务
            while (Constant.map.get(cameraInfo.getAlias())) {
               Frame frame = grabber.grabImage()
               // 死循环不断获取frame对象 然后调用接口 返回json报文保存.....
            }
        } catch (FrameGrabber.Exception e) {
            log.error("FrameGrabber.Exception e" + e.getMessage());
        } catch (IOException e) {
            log.error("启动摄像头线程错误IO: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("启动摄像头线程错误: " + e.getMessage());
        } finally {
    
            if (grabber != null) {
                try {
                    grabber.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            log.info("线程" + Thread.currentThread().getName() + "结束");
        }

    }
}

3. contronller层对摄像头的增删改查

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wd.gansubox.bean.PageVo;
import com.wd.gansubox.bean.Query;
import com.wd.gansubox.bean.QueryCondition;
import com.wd.gansubox.bean.Resp;
import com.wd.gansubox.common.Constant;
import com.wd.gansubox.component.CameraTask;
import com.wd.gansubox.dao.CameraInfoDao;
import com.wd.gansubox.entity.CameraInfo;
import com.wd.gansubox.entity.CameraInfoDetail;
import com.wd.gansubox.exception.RRException;
import com.wd.gansubox.service.CameraInfoDetailService;
import com.wd.gansubox.service.CameraInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
public class CameraInfoController {

    @Autowired
    private CameraInfoDao cameraInfoDao;

    @Autowired
    private CameraInfoService cameraInfoService;

    @Autowired
    private CameraInfoDetailService cameraInfoDetailService;
    @Autowired
    private CameraTask cameraTask;
    private ReentrantLock lockSave = new ReentrantLock();
    private ReentrantLock lockUpdate = new ReentrantLock();
    private ReentrantLock lockRemove = new ReentrantLock();
     /**
     * 保存
     */
    @Log("保存盒子上报信息")
    @ApiOperation("保存")
    @PostMapping("/save")
    @Transactional(rollbackFor = Exception.class)
    public Resp<Object> save(@RequestBody  CameraInfo cameraInfo ) {
        //非空判断
        ...
        //查询数据库摄像头数量是否超过4个
        ...
        // 保存至数据库并且启动线程
        if (lockSave.tryLock()) {
            try {
                // 设置别名 作为摄像头线程的名称 别名只有4个 1111 2222 3333 4444
                if (aliasList.size() == 0) {
                    cameraInfo.setAlias(CameraInfo.ALIAS_1);
                } else {
                    for (int z = CameraInfo.ALIAS_1; z <= CameraInfo.ALIAS_4; z += CameraInfo.ALIAS_1) {
                        if (!aliasList.contains(z)) {
                            cameraInfo.setAlias(z);
                            break;
                        }
                    }
                }
                // 保存数据
                cameraInfoService.save(cameraInfo);
                // 开启线程
                Constant.map.put(cameraInfo.getAlias(), true);
                cameraTask.startCameraThread(cameraInfo);
            } catch (Exception e) {
                throw new RRException("lockSave 发生错误" + e.getMessage());
            } finally {
                lockSave.unlock();
            }
        } else {
            return Resp.fail("请等待其他摄像头启动后再试");
        }
        return Resp.ok(null);
    }

    /**
     * 修改
     */
    @Log("修改盒子初始信息")
    @ApiOperation("修改")
    @PostMapping("/update")
    @Transactional(rollbackFor = Exception.class)
    public Resp<Object> update(@RequestBody  CameraInfo cameraInfo ) {
        // 非空判断
        ...
        //更新摄像头
        try {
            if (lockUpdate.tryLock()) {
                // 更新摄像头
                cameraInfoService.updateById(cameraInfo);
                // 关闭线程
                Constant.map.put(cameraInfo.getAlias(), false);
                // 开启线程
                Constant.map.put(cameraInfo.getAlias(), true);
                cameraTask.startCameraThread(cameraInfo);
            } else {
                return Resp.fail("请等待其他摄像头更新后再试");
            }

        } catch (Exception e) {
            throw new RRException("lockUpdate 发生错误" + e.getMessage());
        } finally {
            lockUpdate.unlock();
        }
        return Resp.ok(null);

    }

    /**
     * 删除
     */
    @Log("删除盒子初始信息")
    @ApiOperation("删除")
    @GetMapping("/delete/{id}")
    @Transactional(rollbackFor = Exception.class)
    public Resp<Object> delete(@PathVariable("id") Long id) {
        CameraInfo cameraInfo = cameraInfoService.getBaseMapper().selectById(id);
        if (cameraInfo == null) {
            return Resp.fail("摄像头未查询到");
        }
        // 删除摄像头
        if (lockRemove.tryLock()) {
            try {
                cameraInfoService.removeById(id);
                // 关闭线程
                Constant.map.put(cameraInfo.getAlias(), false);
            } catch (Exception e) {
                throw new RRException("lockRemove 发生错误" + e.getMessage());
            } finally {
                lockRemove.unlock();
            }
        } else {
            return Resp.fail("请等待其他摄像头删除后再试");
        }
        return Resp.ok(null);
    }
}

4.  实体类

@ApiModel
@Data
@TableName("camera_info")
public class CameraInfo implements Serializable {

    private static final long serialVersionUID = 300926603958321459L;

    public static final int ALIAS_1 = 1111;
    public static final int ALIAS_2 = 2222;
    public static final int ALIAS_3 = 3333;
    public static final int ALIAS_4 = 4444;


    @TableId
    @ApiModelProperty(name = "id", value = "")
    private Long id;
    /**
     * 设备名称
     */
    @ApiModelProperty(name = "deviceName", value = "设备名称")
    @NotEmpty
    private String deviceName;
   
    /**
     * 用户名
     */
    @ApiModelProperty(name = "username", value = "用户名")
    @NotEmpty
    private String username;
    /**
     * 密码
     */
    @ApiModelProperty(name = "password", value = "密码")
    @NotEmpty
    private String password;
    /**
     * ip
     */
    @ApiModelProperty(name = "ip", value = "ip")
    @NotEmpty
    private String ip;
    /**
     * port
     */
    @ApiModelProperty(name = "port", value = "port")
    @NotEmpty
    private String port;
   
    @ApiModelProperty(name = "alias", value = "别名 固定4个")
    private int alias;

   

  

    // 把用户名 密码 ip 拼接成摄像头地址
    public String appendUrl() {
        return "rtsp://" + username + ":" + password + "@" + ip + ":" + port + "/cam/realmonitor?channel=1&subtype=0";
    }


}

5. 常量

/**
 * 参数定义
 *
 * @author yangxiaofeng
 * @since 2020-03-18 10:16:01
 */
public interface Constant {

    Map<Integer, Boolean> map = new ConcurrentHashMap(16);

    /**
     * 初始化线程开关 状态为可开启 启动时调用
     */
    static void initMap() {
        Constant.map.put(CameraInfo.ALIAS_1, true);
        Constant.map.put(CameraInfo.ALIAS_2, true);
        Constant.map.put(CameraInfo.ALIAS_3, true);
        Constant.map.put(CameraInfo.ALIAS_4, true);
    }
}

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值