背景
由于上班的时候会挺忙,看的时候孩子要么是去户外游戏了,要么是午睡,或者你压根没时间看,幼儿园给的直播地址又不能回看,这就让我想到了可以做一个软件,将直播视频流录成视频保存起来,等有时间的时候可以回看,还可以录制其他直播。
需求
那怎么做呢?我简单调研整理了一下该软件的需求。
流程
大致流程是这样
上传到百度网盘后,是这样的
功能实现
后台录制直播视频流关键是通过JavaCV组件来实现,这个组件的包非常大,功能也很强大,可以多了解了解。
1、根据录制时间分片
2、文件按日期命名保存
3、根据指定时间录制
依赖
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.4.4</version> </dependency>
分片录制
/** * 录制视频 * @param inputFile 该地址可以是网络直播/录播地址,也可以是远程/本地文件路径 * @param outputFile 该地址只能是文件地址,如果使用该方法推送流媒体服务器会报错,原因是没有设置编码格式 * @param audioChannel 是否录制音频 1录制 * @param time 录制时间 * @throws Exception * @throws org.bytedeco.javacv.FrameRecorder.Exception */ public void frameRecord(String inputFile, String outputFile, int audioChannel, int time) throws Exception, org.bytedeco.javacv.FrameRecorder.Exception { // 获取视频源 FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile); // 流媒体输出地址,分辨率(长,高),是否录制音频(0:不录制/1:录制) FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); //设置分片 recorder.setFormat("segment"); //生成模式 实时 recorder.setOption("segment_list_flags", "live"); //分片时长 60s recorder.setOption("segment_time", "60"); //锁定分片时长 recorder.setOption("segment_atclocktime", "1"); //用来严格控制分片时长 recorder.setOption("break_non_keyframes", "1"); //设置日志级别 avutil.av_log_set_level(avutil.AV_LOG_ERROR); // 开始取视频源 try { grabber.start(); recorder.start(); Frame frame = null; Date startDate = new Date(); while ((frame = grabber.grabFrame()) != null && DateUtil.between(startDate, new Date(), DateUnit.SECOND) <= time * 60) { recorder.record(frame); } recorder.stop(); grabber.stop(); } finally { if (grabber != null) { grabber.stop(); } } }
分片配置参考地址:https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment
定时录制
各种定时任务都太重麻烦不适合我这个玩玩的项目,一开始就都被pass掉了,后来发现可以用CronTrigger类,这个类可以动态指定cron来运行定时任务,并且可以控制任务停止,简单又好用。
Runnable runnable = () -> { try { log.info("taskId={},开始录制视频", task.getId()); frameRecord(task.getStreamUrl(), prePath + DateUtil.today() + "-%03d" + ".flv", 1, task.getRecordTime()); log.info("taskId={},结束录制视频", task.getId()); } catch (Exception e) { log.warn("录制发生错误", e); } }; CronTrigger cronTrigger = new CronTrigger(task.getRunRule()); //spring的 ThreadPoolTaskScheduler线程池 ScheduledFuture future = threadPoolTaskScheduler.schedule(runnable, cronTrigger);
停止任务
future.cancel(false);