java多线程按照时间段切割视频流片段

package com.chinaunicom.zwboy.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chinaunicom.common.base.convert.BaseConvert;
import com.chinaunicom.common.base.feign.RemoteUserGbCodeService;
import com.chinaunicom.common.base.feign.model.DeviceCommandPtzReqBaseDto;
import com.chinaunicom.common.core.constant.SecurityConstants;
import com.chinaunicom.video.entity.Lives;
import com.chinaunicom.video.entity.SvAircraftVideoRecord;
import com.chinaunicom.video.service.LivesService;
import com.chinaunicom.video.util.RedisUtil;
import com.chinaunicom.video.util.WoYunOssUtils;
import com.chinaunicom.zwboy.model.PushData;
import com.chinaunicom.zwboy.model.PushTask;
import com.chinaunicom.zwboy.service.*;
import com.chinaunicom.zwboy.service.impl.SaveRecordThreadManagerImpl;
import com.chinaunicom.zwboy.util.FileOperUtils;
import com.lh.service.SaveRecord;
import com.lh.service.SliceType;
import com.lh.service.impl.SaveRecordImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.*;

@Slf4j
@RestController
//@RequestMapping("/pushAsync")
public class PushLhController {

    @Autowired
    private SaveRecordThreadManagerImpl saveRecordThreadManagerImpl;

    @Autowired
    private PushAsyncService pushAsyncService;
    @Autowired
    private PushLhService pushLhService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private WoYunOssUtils woYunOssUtils;

    @Autowired
    private LivesService livesService;
    @Autowired
    private MyTaskService myTaskService;


    @Value("${linux.path}")
    private String linuxPath;

    @Value("${redisTime.expiredTime}")
    private Integer expiredTime;



    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;


    capableBeanFactory.autowireBean(forwardTask);


    // 创建一个定长线程池,支持定时及周期性任务执行,建立一个延时任务,1000毫秒之后执行
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

    @PostMapping(value = "/pushers")
    @ResponseBody
    public String pusherAndStop(HttpServletRequest request, HttpServletResponse response, @RequestBody String param) {
        log.info("PushLhController PusherAndStop() param="+Thread.currentThread().getName()+" "+param);
        log.info("PushLhController PusherAndStop() linuxPath="+linuxPath);
        log.info("PushLhController PusherAndStop() expiredTime="+expiredTime);
        JSONObject jsonObject = JSONObject.parseObject(param);
        String stream = jsonObject.getString("stream");
        QueryWrapper<Lives> queryWrapperByid = BaseConvert.buildDelQueryWrapper();
        queryWrapperByid.eq("c_id", stream);
        Lives lives = livesService.getOne(queryWrapperByid);
        log.info("PushLhController PusherAndStop() lives="+ JSON.toJSONString(lives));
        if(lives==null){//无视
            return "0";
        }
        try {
            //校验推送流还是关闭流
            if (jsonObject.getString("action").equals("on_publish")) {
                PushData pushTask = new PushData();
                String uuid = UUID.randomUUID().toString().replace("-", "");
                pushTask.setId(uuid);
                pushTask.setPushSrcUrl(lives.getRtmpUrl());//推流源地址/改成播放地址
                pushTask.setPushDescUrl(linuxPath);
                pushTask.setStream(stream);
                String pushTaskJson = JSONUtil.toJsonStr(pushTask);
                log.info("PushLhController PusherAndStop() on_publish pushTaskJson="+pushTaskJson);
                //生成磁盘录像文件 //上传沃云成功并保存本地表记录
                //pushLhService.asyncStart(pushTask);
                String pushSrcUrl = pushTask.getPushSrcUrl();
                String pushDescUrl = pushTask.getPushDescUrl();
                String streamId = pushTask.getStream();
                // 参数"1234", "rtmp://172.16.10.196:1935/live/5e7367809f32448682177561af153f9d", "D:/tmp/", true, SliceType.DURATION, 10
                saveRecordThreadManagerImpl.start(streamId,pushSrcUrl,pushDescUrl,true, SliceType.DURATION, expiredTime);
                long initialDelay =  3L; //定时任务延时启动30秒
                long period = expiredTime+3L; //定时任务时间间隔5分钟,
                log.info("PushLhController PusherAndStop() period="+period);
                executor.scheduleAtFixedRate(myTaskService,initialDelay ,period, TimeUnit.SECONDS);
                //SaveRecord saveRecord = new SaveRecordImpl();
                //saveRecord.start("1234", "rtmp://172.16.10.196:1935/live/5e7367809f32448682177561af153f9d","D:/tmp/", true, SliceType.DURATION, 30);
                return "0";
            } else if (jsonObject.getString("action").equals("on_unpublish")) {
                //停止推流
                if(StringUtils.isNotBlank(stream)) {
                    pushLhService.asyncStop(stream);
                }
                return "0";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "0";
    }

}



package com.chinaunicom.zwboy.service.impl;

import com.chinaunicom.zwboy.service.SaveRecordService;
import com.chinaunicom.zwboy.util.ApplicationContextUtil;
import com.lh.service.SliceType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class SaveRecordThreadManagerImpl  {

    Map<String, SaveRecordService> saveRecordMap = new HashMap<>();


    /**
     *
     * @param id 线程id
     * @param inputFile 网络输入流 rtmp
     * @param outputFile 输出mp4文件
     * @param needAudio 是否录音
     * @param sliceType
     * @param arg 多久生成一个mp4 单位秒
     */


    @Async
    public void start(String id, String inputFile, String outputFile, Boolean needAudio, SliceType sliceType, int arg) {
        SaveRecordService saveRecord;
        synchronized (saveRecordMap) {
            saveRecord = saveRecordMap.get(id);
            if (saveRecord == null) {
                saveRecord = new SaveRecordServiceImpl();
                //saveRecord =  ApplicationContextUtil.getBean("saveRecordServiceImpl");
                saveRecordMap.put(id, saveRecord);
            }
        }
        saveRecord.start(id, inputFile, outputFile, needAudio, sliceType, arg);
    }


    public void stop(String id) {
        SaveRecordService saveRecord;
        synchronized (saveRecordMap) {
            saveRecord = saveRecordMap.get(id);
            saveRecordMap.remove(id);
        }
        if (saveRecord != null)
            saveRecord.stop(id);
    }



}




package com.chinaunicom.zwboy.service.impl;

import com.chinaunicom.zwboy.service.NewRecordService;
import com.chinaunicom.zwboy.service.SaveRecordService;
import com.lh.service.SliceType;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@Service("saveRecordServiceImpl")
public class SaveRecordServiceImpl implements SaveRecordService {
    private FFmpegFrameGrabber grabber;
    private FFmpegFrameRecorder recorder;


    private NewRecordService newRecordService;

    private String id;
    private Boolean isStart = false;
    private int seconds;

    @Value("5000000")
    private String stimeout;        // 连接输入视频流超时, us

    @Value("3000000")
    private String timeout;         // tcp/udp超时, us

    private String inputFile;       // 输入视频流地址
    private String outPath;         // 输出路径
    private String outputFile;      // 输出文件
    private Boolean needAudio;      // 是否需要音频
    private Date startTime;
    SliceType sliceType;            // 切片类型
    int sliceArg;                   // 切片参数
    Frame lastKeyFrame;
    Integer index = 0;


    @Override
    public void start(String id, String inputFile, String outPath, Boolean needAudio, SliceType sliceType, int arg) {
        synchronized (isStart) {
            if (isStart)
                return;
            this.id = id;
            this.inputFile = inputFile;
            if (outPath.charAt(outPath.length() - 1) != '/')
                this.outPath = outPath + "/";
            else
                this.outPath = outPath;
            this.needAudio = needAudio;
            this.sliceType = sliceType;
            this.sliceArg = arg;
            this.isStart = true;
        }
        save_record();
    }

    @Override
    public void stop(String id) {
        synchronized (isStart) {
            if (!isStart)
                return;
            isStart = false;
        }
    }

    @Async
    public void save_record() {
        while (isStart) {
            try {
                // 打开视频流
                if (grabber == null) {
                    try {
                        log.info("打开流地址={}", inputFile);
                        grabber = new FFmpegFrameGrabber(inputFile);
                        grabber.setOption("stimeout", stimeout);
                        grabber.setOption("timeout", timeout);
                        grabber.start();
                    } catch (FrameGrabber.Exception e) {
                        log.error("grabber打开失败, {}", e.toString());
                        grabber = null;
                        continue;
                    }
                }
                getFilename();
                recorder = new FFmpegFrameRecorder(outputFile, grabber.getImageWidth(), grabber.getImageHeight(),
                        needAudio ? 1 : 0);
                recorder.start();
                startTime = new Date();
                Frame frame = null;
                try {
                    while (isStart) {
                        if (lastKeyFrame == null)
                            frame = grabber.grabFrame();
                        else {
                            frame = lastKeyFrame;
                            lastKeyFrame = null;
                        }
                        if (needSlice(frame)) {
                            lastKeyFrame = frame;
                            saveRecord();
                            break;
                        }
                        if (frame != null) {
                            recorder.record(frame);
                        }
                    }
                } catch (FrameRecorder.Exception e) {
                    log.error("save_record 1= 写录像出错, {}", e.toString());
                }
                saveRecord();
            } catch (FrameGrabber.Exception | FrameRecorder.Exception e) {
                log.error("save_record 2= 初始化失败", e.toString());
                e.printStackTrace();
            } finally {
                saveRecord();
            }
        }
    }

    // 判断是否需要切片
    boolean needSlice(Frame frame) {
        switch (sliceType) {
            case DURATION:
                return sliceArg <= getDuration() && frame.keyFrame;
            case SIZE:
                break;
            default:
                assert false;
        }
        return false;
    }

    // 保存当前的录像
    void saveRecord() {
        try {
            if (recorder != null) {
                recorder.stop();
                Date cur = new Date();
                log.info("saveRecourdFile=新录像生成={}, startTime={}, duration={}", outputFile, startTime, getDuration());
                //log.info("deleteRecourdFile=新录像删除={}, startTime={}, duration={}", outputFile, startTime, getDuration());
                //newRecordService.newRocord(id, outputFile, startTime, getDuration());
                newRecordService=new NewRecordServiceImpl();
                newRecordService.delNewRocord(id, outputFile, startTime, getDuration());
                recorder = null;
            }
        } catch (FrameRecorder.Exception e) {
            log.info("saveRecord FrameRecorder:"+e.getMessage());
            e.printStackTrace();
        }
    }

    long getDuration() {
        Date cur = new Date();
        return (cur.getTime() - startTime.getTime()) / 1000;
    }

    void getFilename() {
        DateFormat df = new SimpleDateFormat("yyMMddHHmmssSSS"); // add S if you need millisecon// ds
        String filename = df.format(new Date());
        outputFile = outPath + filename + "_" + id + ".mp4";
        index += 1;
    }


}





package com.chinaunicom.zwboy.service.impl;

import com.chinaunicom.video.service.LivesService;
import com.chinaunicom.video.util.WoYunOssUtils;
import com.chinaunicom.zwboy.service.NewRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Date;


@Slf4j
@Service
public class NewRecordServiceImpl implements NewRecordService {

    @Value("${linux.path}")
    private String linuxPath;

    @Value("${redisTime.expiredTime}")
    private Long expiredTime;

    @Autowired
    private WoYunOssUtils woYunOssUtils;

    @Autowired
    private LivesService livesService;
    int count=0;

    @Override
    public void newRocord(String id, String fileName, Date startTime, long duration) {
        // 定时任务业务逻辑
        count++;
        log.info("NewRecordServiceImpl newRocord fileName:" + fileName+"id:"+id+" count:"+count++);
        /*File child = new File(fileName);
        if (child.length() <= 258) {
            return;
        }
        String stream = id;
        log.info("NewRecordServiceImpl toUpload2 fileName=" + fileName);
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(child);
            DateFormat df = new SimpleDateFormat(FileOperUtils.dateFormatStr); // add S if you need millisecon// ds
            String filename = df.format(new Date());
            String fileName2 = filename + "-" + stream + ".mp4";
            String url = woYunOssUtils.uploadFileToMp4(inputStream, fileName2, child.length());
            PushData pushData = new PushData();
            String uuid = UUID.randomUUID().toString().replace("-", "");
            pushData.setId(uuid);
            pushData.setPushDescUrl(fileName2);
            pushData.setStream(stream);
            pushData.setRecordCreateTime(new Date());
            pushData.setRecordName(fileName2);
            pushData.setRecordStorePath(url);
            log.info("NewRecordServiceImpl toUpload2 pushData=" + JSON.toJSONString(pushData));
            QueryWrapper<Lives> queryWrapperByid = BaseConvert.buildDelQueryWrapper();
            queryWrapperByid.eq("c_id", pushData.getStream());
            Lives lives = livesService.getOne(queryWrapperByid);
            log.info("NewRecordServiceImpl toUpload() lives:" + JSON.toJSONString(lives));
            SvAircraftVideoRecord svAircraftVideoRecord = new SvAircraftVideoRecord();
            svAircraftVideoRecord.setSvLiveId(lives.getCId());
            svAircraftVideoRecord.setFileSize(child.length());
            svAircraftVideoRecord.setRecordName(pushData.getRecordName());
            svAircraftVideoRecord.setRecordStorePath(pushData.getRecordStorePath());
            svAircraftVideoRecord.setRecordCreateTime(pushData.getRecordCreateTime());
            log.info("NewRecordServiceImpl toUpload2 svAircraftVideoRecord=" + JSON.toJSONString(svAircraftVideoRecord));
            //livesService.saveSvAircraftVideoRecord(svAircraftVideoRecord);
            //上传完沃云删除服务器本地文件
            //child.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }*/
    }

    @Override
    public void delNewRocord(String id, String filename, Date startTime, long duration) {
        //log.info("NewRecordServiceImpl delNewRocord :" + filename);
        File root = new File(filename);
        if (root.isFile()) {
            if(root.length()<=258) {
                root.delete();
            }
        }
    }
}




package com.chinaunicom.zwboy.service;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chinaunicom.common.base.convert.BaseConvert;
import com.chinaunicom.video.entity.Lives;
import com.chinaunicom.video.entity.SvAircraftVideoRecord;
import com.chinaunicom.video.service.LivesService;
import com.chinaunicom.video.util.WoYunOssUtils;
import com.chinaunicom.zwboy.model.PushData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Service
public class MyTaskService implements Runnable{

    @Value("${linux.path}")
    private String linuxPath;

    @Value("${redisTime.expiredTime}")
    private Long expiredTime;

    @Autowired
    private WoYunOssUtils woYunOssUtils;

    @Autowired
    private LivesService livesService;

    @Override
    public void run() {
        // 定时任务业务逻辑
        log.info("MyTaskService runLinuxPath :"+linuxPath);
        File root = new File(linuxPath);
        if (root != null && root.exists()) {
            if (root.isDirectory()) {
                File[] children = root.listFiles();
                if (children != null) {
                    for (File child : children) {
                        String fileName = child.getName();
                        if (child.isFile() && fileName.endsWith("mp4")) {
                            long fileSize = child.length();
                            if(fileSize<=258){
                                child.delete();
                            }else {
                                String[] fileNames = fileName.split("_");
                                String streamId = fileNames[1];
                                Integer nameIndex2 = streamId.lastIndexOf(".");
                                String stream = streamId.substring(0, nameIndex2);
                                log.info("MyTaskService child.length() :" + fileSize + "fileName:" + fileName);
                                InputStream inputStream = null;
                                try {
                                    if(fileSize<=258){
                                        child.delete();
                                        continue;
                                    }
                                    inputStream = new FileInputStream(child);
                                    String url = woYunOssUtils.uploadFileToMp4(inputStream, fileName, fileSize);
                                    PushData pushData = new PushData();
                                    String uuid = UUID.randomUUID().toString().replace("-", "");
                                    pushData.setId(uuid);
                                    pushData.setPushDescUrl(fileName);
                                    pushData.setStream(stream);
                                    pushData.setRecordCreateTime(new Date());
                                    pushData.setRecordName(fileName);
                                    pushData.setRecordStorePath(url);
                                    log.info("MyTaskService toUpload2 pushData=" + JSON.toJSONString(pushData));
                                    QueryWrapper<Lives> queryWrapperByid = BaseConvert.buildDelQueryWrapper();
                                    queryWrapperByid.eq("c_id", pushData.getStream());
                                    Lives lives = livesService.getOne(queryWrapperByid);
                                    log.info("MyTaskService toUpload2() lives:" + JSON.toJSONString(lives));
                                    if(lives!=null) {
                                        SvAircraftVideoRecord svAircraftVideoRecord = new SvAircraftVideoRecord();
                                        svAircraftVideoRecord.setSvLiveId(lives.getCId());
                                        svAircraftVideoRecord.setSvLiveName(lives.getName());
                                        svAircraftVideoRecord.setFileSize(fileSize);
                                        svAircraftVideoRecord.setRecordName(pushData.getRecordName());
                                        svAircraftVideoRecord.setRecordStorePath(pushData.getRecordStorePath());
                                        svAircraftVideoRecord.setRecordCreateTime(pushData.getRecordCreateTime());
                                        log.info("MyTaskService toUpload2 svAircraftVideoRecord=" + JSON.toJSONString(svAircraftVideoRecord));
                                        livesService.saveSvAircraftVideoRecord(svAircraftVideoRecord);
                                        //上传完沃云删除服务器本地文件
                                        child.delete();
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值