/****************************************************************************** * 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 queryVideoLiveReplay(String videoLiveId){ StringBuffer hql = new StringBuffer(500); hql.append("from MediaVideoLiveReplay where videoLiveId=? and deleteFlag is false "); List 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(){ @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); } }