/******************************************************************************
|
* Copyright (C) 2015 Shenzhen Penguin Network Technology Co., Ltd
|
* All Rights Reserved.
|
* 本软件为深圳市企鹅网络科技有限公司开发研制。未经本公司正式书面同意,其他任何个人、团体
|
* 不得使用、复制、修改或发布本软件.
|
*****************************************************************************/
|
|
package com.qxueyou.scc.teach.live.service.impl;
|
|
import java.io.File;
|
import java.io.FilenameFilter;
|
import java.util.Arrays;
|
import java.util.Calendar;
|
import java.util.Comparator;
|
import java.util.GregorianCalendar;
|
import java.util.List;
|
import java.util.concurrent.TimeUnit;
|
|
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.lang3.StringUtils;
|
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.Logger;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Service;
|
|
import com.qxueyou.scc.base.service.ICacheService;
|
import com.qxueyou.scc.base.service.impl.CommonAppService;
|
import com.qxueyou.scc.base.util.CollectionUtils;
|
import com.qxueyou.scc.base.util.TraceUtils;
|
import com.qxueyou.scc.base.util.UUIDUtils;
|
import com.qxueyou.scc.config.SccConfig;
|
import com.qxueyou.scc.teach.live.model.MediaVideoLive;
|
import com.qxueyou.scc.teach.live.model.MediaVideoLiveReplay;
|
import com.qxueyou.scc.teach.live.service.IMediaVideoLivePlayBackService;
|
import com.qxueyou.scc.teach.live.utils.FfmpegFileVO;
|
import com.qxueyou.scc.teach.live.utils.FfmpegMediaHelper;
|
import com.qxueyou.scc.teach.live.utils.FfmpegVideoInfo;
|
|
@Service
|
public class MediaVideoLivePlayBackService extends CommonAppService implements IMediaVideoLivePlayBackService {
|
|
private static final Logger log = LogManager.getLogger(MediaVideoLivePlayBackService.class);
|
|
//直播回放队列
|
public static final String LIVE_PLAYBACK_LST = "LIVE_PLAYBACK_LST";
|
|
//直播回放全局锁
|
public static final String LIVE_PLAYBACK_LOCK = "LIVE_PLAYBACK_LOCK";
|
|
@Autowired
|
SccConfig cfg;
|
|
@Autowired
|
ICacheService cacheService;
|
|
@Autowired
|
private StringRedisTemplate redisTemplate;
|
|
@Override
|
public List<MediaVideoLiveReplay> queryVideoLiveReplay(String videoLiveId){
|
StringBuffer hql = new StringBuffer(500);
|
hql.append("from MediaVideoLiveReplay where videoLiveId=? and deleteFlag is false ");
|
List<Object> params = CollectionUtils.newList(videoLiveId);
|
|
hql.append(" order by orderNo asc");
|
return this.find(hql.toString(),params, MediaVideoLiveReplay.class);
|
}
|
|
//生成回放(需修改为晚上2:00 开始执行)
|
@Scheduled(cron = " 0 0/2 * * * ?")
|
protected void doTimer(){
|
if(this.lock()){
|
String liveId = null;
|
try {
|
liveId = cacheService.lstLeftPop(LIVE_PLAYBACK_LST);
|
|
if(StringUtils.isEmpty(liveId)){
|
return ;
|
}
|
|
//判断是否是回放状态
|
MediaVideoLive live = this.read(MediaVideoLive.class, liveId);
|
|
if(live.getStatus()==MediaVideoLive.STATUS_LIVE_DOWNLOAD){
|
|
log.info("doTimer live: "+live.getVideoLiveId());
|
|
this.doVideoLivePlayBack(live);
|
|
//更新状态
|
live.setStatus(MediaVideoLive.STATUS_LIVE_REVIEW);
|
this.save(live);
|
}
|
} catch (Exception e) {
|
//重新处理
|
if(StringUtils.isNotEmpty(liveId)){
|
cacheService.lstRightPush(LIVE_PLAYBACK_LST,liveId);
|
}
|
}finally{
|
this.releaseLock();
|
}
|
}
|
}
|
|
@Override
|
public void testPlayBack(String liveId){
|
//判断是否是回放状态
|
MediaVideoLive live = this.read(MediaVideoLive.class, liveId);
|
|
if(live.getStatus()==MediaVideoLive.STATUS_LIVE_DOWNLOAD){
|
try {
|
this.doVideoLivePlayBack(live);
|
|
//更新状态
|
live.setStatus(MediaVideoLive.STATUS_LIVE_REVIEW);
|
this.save(live);
|
|
} catch (Exception e) {
|
//重新处理
|
cacheService.lstRightPush(LIVE_PLAYBACK_LST,liveId);
|
}
|
}
|
}
|
|
|
public void doVideoLivePlayBack(MediaVideoLive videoLive) throws Exception{
|
//读取原文件
|
File srcRootFile = new File(cfg.getSrcLivePath());
|
|
String[] fileNames= srcRootFile.list(new FilenameFilter(){
|
@Override
|
public boolean accept(File dir, String name) {
|
return name.startsWith(videoLive.getWyLiveNumber());
|
}
|
});
|
|
log.info("doVideoLivePlayBack fileNames: "+fileNames==null?"xxx":Arrays.toString(fileNames));
|
|
//排序
|
Arrays.sort(fileNames, new Comparator<String>(){
|
@Override
|
public int compare(String o1, String o2) {
|
if(StringUtils.isNotEmpty(o1) && StringUtils.isNotEmpty(o2)){
|
return getStartTime(o1).compareTo(getStartTime(o2));
|
}
|
return 0;
|
}
|
});
|
|
//读取
|
if(fileNames!=null && fileNames.length>0){
|
short order = 1 ;
|
for(String srcFileName:fileNames){
|
log.info("doCreateVideoLiveReplay srcFileName: "+srcFileName);
|
doCreateVideoLiveReplay(videoLive,srcFileName,order++); //生成回放
|
}
|
}
|
|
//清理源文件
|
if(fileNames!=null && fileNames.length>0){
|
for(String srcFileName:fileNames){
|
log.info("删除原文件:filePath"+srcRootFile.getAbsolutePath().concat(File.separator).concat(srcFileName));
|
FileUtils.deleteQuietly(new File(srcRootFile.getAbsolutePath().concat(File.separator).concat(srcFileName)));
|
}
|
}
|
}
|
|
|
//生成VideoLiveReplay信息
|
private String doCreateVideoLiveReplay(MediaVideoLive videoLive,String srcFileName,short order) throws Exception{
|
//视频文件后缀
|
String mediaSuffix = srcFileName.substring(srcFileName.lastIndexOf("."));
|
|
//回放地址
|
String dstPath =this.generateDstPath(videoLive.getVideoLiveId(), mediaSuffix);
|
|
File srcFile = new File(cfg.getSrcLivePath() + srcFileName);
|
|
File dstFileDir = new File(cfg.getResRootPath().concat(dstPath)).getParentFile();
|
|
//创建目录
|
dstFileDir.mkdirs();
|
|
log.info("开始生成 doCreateVideoLiveReplay: srcFile"+srcFile.getAbsolutePath());
|
|
String dstFile = this.convertFlvToMp4(srcFile, dstFileDir);
|
|
log.info("结束生成 doCreateVideoLiveReplay: dstFile"+ dstFile);
|
|
//获取视频时长
|
long playTime = this.getMediaPlayTime(srcFile.getAbsolutePath());
|
|
MediaVideoLiveReplay liveReplay = new MediaVideoLiveReplay();
|
liveReplay.setName((videoLive.getName() + "-" + order + mediaSuffix).replace(".flv", ".mp4"));
|
liveReplay.setVideoLiveId(videoLive.getVideoLiveId());
|
liveReplay.setOrderNo(order);
|
liveReplay.setBeginTime(this.getStartTime(srcFileName));
|
liveReplay.setEndTime(this.getStartTime(srcFileName)+playTime);
|
liveReplay.setDuration(playTime);
|
liveReplay.setInitialSize(srcFile.length());
|
liveReplay.setUrl(dstFile.substring(dstFile.indexOf("live")).replace("\\", "/"));
|
TraceUtils.setCreateTrace(liveReplay);
|
this.save(liveReplay);
|
|
return liveReplay.getLiveReplayId();
|
}
|
|
|
|
/**
|
* 获取文件目标路径
|
*
|
* @param name
|
* @return
|
*/
|
private String generateDstPath(String liveId,String suffix) {
|
StringBuffer path = new StringBuffer(256);
|
path.append("live");
|
Calendar now = new GregorianCalendar();
|
|
path.append('/');
|
path.append(now.get(Calendar.YEAR));
|
path.append(StringUtils.leftPad(String.valueOf(now.get(Calendar.MONTH)), 2, '0'));
|
path.append('/');
|
path.append(now.get(Calendar.DAY_OF_MONTH));
|
path.append('/');
|
path.append(liveId);
|
path.append('/');
|
path.append(UUIDUtils.UUID());
|
path.append(suffix);
|
|
return path.toString();
|
}
|
|
|
private long getMediaPlayTime(String filePath) throws Exception{
|
FfmpegFileVO fileVO = new FfmpegFileVO();
|
fileVO.setInputPath(filePath);
|
FfmpegVideoInfo videoInfo = new FfmpegVideoInfo();
|
FfmpegMediaHelper.mediaInfo(fileVO, videoInfo);
|
return videoInfo.getPlayTime();
|
}
|
|
private String convertFlvToMp4(File srcFile,File dstFile) throws Exception{
|
// 获取直播视频片段信息
|
FfmpegFileVO videoFileVO = new FfmpegFileVO();
|
videoFileVO.setInputPath(srcFile.getPath());
|
|
// 转码视频
|
videoFileVO.setOutputPath(dstFile.getPath() + File.separator);
|
|
return FfmpegMediaHelper.converUploadVideoToLiveMp4(videoFileVO, new FfmpegVideoInfo());
|
|
//copy备份原片段(暂不备份)
|
// FileUtils.copyFile(srcFile, dstFile);
|
}
|
|
|
//格式: d72de336c7ed45beb14641a79074f9bb-1541497652-rec , 时间戳单位: 秒
|
private Long getStartTime(String filePath){
|
return Long.valueOf(filePath.split("-")[1]);
|
}
|
|
private boolean lock(){
|
boolean lockResult = redisTemplate.opsForValue().setIfAbsent(LIVE_PLAYBACK_LOCK, "lock");
|
//可能发生死锁,需要手动清除锁
|
if(lockResult==true){
|
redisTemplate.expire(LIVE_PLAYBACK_LOCK, 6, TimeUnit.HOURS);
|
}
|
return lockResult;
|
}
|
|
private void releaseLock(){
|
redisTemplate.delete(LIVE_PLAYBACK_LOCK);
|
}
|
|
}
|