/******************************************************************************
|
* 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);
|
}
|
|
}
|