自己做笔记,以防止忘记了;原理是根据url将视频先下载到本地,再解析视频
需要额外下载的java jar包地址:http://www.sauronsoftware.it/projects/jave/download.php
一:多线程实现接口
import java.util.concurrent.Callable;
public abstract class AbstractWorker<T> implements Callable {
public abstract String taskName();
@Override
public T call() throws Exception {
return innerCall();
}
public abstract T innerCall() throws Exception;
}
二:多线程继承
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.MultimediaInfo;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import com.vivo.videocore.common.facade.domain.req.GetVideoPlayUrlReq;
import com.vivo.videocore.common.facade.domain.resp.CommonResp;
import com.vivo.videocore.common.facade.inter.BackendVideoFacade;
import com.vivo.videomng.es.domain.TCommentEntity;
import com.vivo.videomng.es.domain.VideoEntity;
import com.vivo.videomng.es.util.EsConstantsUtil;
import com.vivo.videomng.receiver.AbstractWorker;
import com.vivo.videomng.receiver.util.CoverUtil;
import com.vivo.videomng.web.po.VideoFBLPo;
import com.vivo.videomng.web.po.VideoWorkerPo;
///video/resolutionRatio/main.do
@Slf4j
public class QueryVideoWorker extends AbstractWorker{
private static final String video_index =EsConstantsUtil.VideoIndexAlias;
private static final String video_type =EsConstantsUtil.VideoIndexType;
private Logger logger = LoggerFactory.getLogger(QueryVideoWorker.class);
private String videoId;
private String path;
private BackendVideoFacade backendVideoFacade;
private ElasticsearchTemplate elasticsearchTemplate;
@Override
public String taskName() {
return this.getClass().getSimpleName();
}
@Override
public Object innerCall() throws Exception {
VideoWorkerPo videoPo = new VideoWorkerPo();
GetVideoPlayUrlReq paramGetVideoPlayUrlReq = new GetVideoPlayUrlReq();
paramGetVideoPlayUrlReq.setVideoId(videoId);
CommonResp<String> resp = backendVideoFacade.queryVideoPlayUrl(paramGetVideoPlayUrlReq);
File source=null;
if (resp != null && resp.getRetcode() == 0) {
String url = resp.getData();
try {
//调用下载视频方法
getRemoteFile(url,path);
// 下载到本地的视频
source = new File(path);
Encoder encoder = new Encoder();
// 获取多媒体信息对象
MultimediaInfo m = encoder.getInfo(source);
//获取数据设置对象
VideoFBLPo vPo =new VideoFBLPo();
vPo.setVideoId(videoId);
vPo.setUrl(url);
vPo.setHeight(m.getVideo().getSize().getHeight());
vPo.setWeight(m.getVideo().getSize().getWidth());
//查询es 拿到标题和来源
VideoEntity videoEntity = queryByVideoId(videoId);
if(videoEntity!=null){
if(StringUtils.isNotBlank(videoEntity.getTitle())){
vPo.setTitle(videoEntity.getTitle());
}else {
vPo.setTitle("");
}
if(StringUtils.isNotBlank(videoEntity.getPartner())){
vPo.setSource(videoEntity.getPartner());
}else {
vPo.setSource("");
}
String picUrl = CoverUtil.extractCoverUrl(videoEntity.getCoverUrls(), videoEntity.getVideoType());
vPo.setPicUrl(picUrl);
}
videoPo.setvPo(vPo);
source.delete();
} catch (IOException e) {
logger.error("视频url异常 下载视频失败,videoId={}",videoId,e);
videoPo.setMsg("视频url异常 下载视频失败"+videoId);
}finally{
if(null != source){
source.delete();
}
}
}else {
logger.error("dubbo查询播放地址失败!!Retcode:"+resp.getRetcode()+" 视频id:"+videoId);
videoPo.setMsg("dubbo查询播放地址失败!!Retcode:"+resp.getRetcode()+" 视频id:"+videoId);
}
if(source != null){
source.delete();
}
return videoPo;
}
public QueryVideoWorker( String videoId, String path,
BackendVideoFacade backendVideoFacade,ElasticsearchTemplate elasticsearchTemplate) {
this.videoId = videoId;
this.path = path;
this.backendVideoFacade = backendVideoFacade;
this.elasticsearchTemplate = elasticsearchTemplate;
}
private boolean getRemoteFile(String strUrl, String fileName)
throws IOException {
URL url = new URL(strUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
DataInputStream input = new DataInputStream(conn.getInputStream());
DataOutputStream output = new DataOutputStream(new FileOutputStream(
fileName));
byte[] buffer = new byte[1024 * 8];
int count = 0;
while ((count = input.read(buffer)) > 0) {
output.write(buffer, 0, count);
}
output.close();
input.close();
return true;
}
//根据视频id 查询es获得视频封面等信息
private VideoEntity queryByVideoId(String videoId) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// SortBuilder sortBuilder =
// SortBuilders.fieldSort("commentTime").order(SortOrder.DESC);
// Pageable pageable = new PageRequest(start/length, length);
boolQuery.must(QueryBuilders.termQuery("videoId", videoId));
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withIndices(video_index).withTypes(video_type)
.withQuery(boolQuery).build();
List<VideoEntity> list = elasticsearchTemplate.queryForList(query,
VideoEntity.class);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
} else {
return null;
}
}
}
三:controller层
package com.vivo.videomng.web.controller;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.TempFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
///video/resolutionRatio/main.do
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.vivo.commonmng.core.model.UserPrincipalHolder;
import com.vivo.framework.redis.JedisTemplate;
import com.vivo.framework.spring.webmvc.CommonVO;
import com.vivo.videocore.common.facade.domain.es.ESVideoBean;
import com.vivo.videocore.common.facade.domain.req.GetVideoPlayUrlReq;
import com.vivo.videocore.common.facade.domain.resp.CommonResp;
import com.vivo.videocore.common.facade.inter.BackendVideoFacade;
import com.vivo.videomng.common.log.LogParam;
import com.vivo.videomng.common.log.Loggable;
import com.vivo.videomng.common.util.MenuUtils;
import com.vivo.videomng.service.OperateRecordService;
import com.vivo.videomng.web.controller.work.QueryVideoWorker;
import com.vivo.videomng.web.po.VideoFBLPo;
import com.vivo.videomng.web.po.VideoWorkerPo;
import com.vivo.videomng.web.vo.DataTableVO;
/**
* 查询视频分辨率
* @author Administrator
*
*/
@Controller
@RequestMapping("/video/resolutionRatio")
public class ShortVideoResolutionRatioController {
@Autowired
private BackendVideoFacade backendVideoFacade;
@Autowired
@Qualifier("asyncVideoTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private OperateRecordService operateRecordService;
@Autowired
private JedisTemplate jedisTemplate;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
private Logger logger = LoggerFactory
.getLogger(ShortVideoResolutionRatioController.class);
@RequestMapping("/main.do")
public String toShortMainPage(HttpSession session) {
return "videoFBL/shortmain";
}
@RequestMapping("/list.do")
@ResponseBody
@Loggable(module = MenuUtils.VIDEO_FBL_SHORT, action = MenuUtils.ACTION_QUERY)
public DataTableVO<List<VideoFBLPo>> list(Integer start, Integer length,
Integer draw, @LogParam( name ="查询内容")String videoIdString, HttpServletRequest request) {
DataTableVO<List<VideoFBLPo>> result = new DataTableVO<List<VideoFBLPo>>();
result.setDraw(draw);
List<VideoFBLPo> list = new ArrayList<VideoFBLPo>();
String courseFile = null;
String[] tmp = null;
//此处try 是保证 删除临时视频文件一定会执行!!
try {
// 第一次参数为空 直接返回
if (StringUtils.isBlank(videoIdString)) {
result.setRecordsTotal(0);
result.setData(list);
return result;
}
// 暂时存储路径
File directory = new File("");// 参数为空
//String courseFile = null;
try {
courseFile = directory.getCanonicalPath();
} catch (IOException e1) {
e1.printStackTrace();
}
// System.out.println(courseFile);//F:\workspace\vivovideo\tet
// String path=courseFile+"/test.mp4";
try {
tmp = videoIdString.split("@");
} catch (Exception e) {
logger.error("视频id解析异常", e);
result.setRecordsTotal(0);
result.setData(list);
return result;
}
// 调用线程池
List<FutureTask<VideoWorkerPo>> tasks = new ArrayList<FutureTask<VideoWorkerPo>>();
for (String videoId : tmp) {
//查缓存
//去除id前后的空格 换行/n
videoId=videoId.trim();
String key = "VIDEO:URL_INFO:SHORT:" + videoId ;
String value = jedisTemplate.get(key);
if (StringUtils.isNotBlank(value)) {
VideoFBLPo rePo = JSON.parseObject(value, VideoFBLPo.class);
list.add(rePo);
}else {
//redis 没有查到
// 视频保存路径
String path = courseFile + File.separator + videoId.hashCode() + ".mp4";
QueryVideoWorker q = new QueryVideoWorker(videoId, path,
backendVideoFacade,elasticsearchTemplate);
FutureTask<VideoWorkerPo> r = new FutureTask<>(q);
threadPoolTaskExecutor.submit(r);
tasks.add(r);
}
}
// 线程池执行完 取数据
VideoWorkerPo videoWorkerPo = null;
if (CollectionUtils.isNotEmpty(tasks)) {
for (FutureTask<VideoWorkerPo> futureTask : tasks) {
try {
videoWorkerPo = futureTask.get();
if (StringUtils.isNotBlank(videoWorkerPo.getMsg())) {
result.setError(videoWorkerPo.getMsg());
} else {
list.add(videoWorkerPo.getvPo());
//保存redis 七天
String key = "VIDEO:URL_INFO:SHORT:" + videoWorkerPo.getvPo().getVideoId() ;
jedisTemplate.set(key, JSON.toJSONString(videoWorkerPo.getvPo()));
jedisTemplate.expire(key, 86400*7);
}
} catch (Exception e) {
logger.error("futureTask.get()异常", e);
result.setError("futureTask.get()异常");
}
}
}
result.setRecordsTotal(tmp.length);
result.setData(list);
//保存操作记录
String content = videoIdString;
operateRecordService.insertOperate(MenuUtils.VIDEO_FBL_SHORT, MenuUtils.ACTION_QUERY, content);
String username = UserPrincipalHolder.get().getLoginName();
logger.info("短视频查询视频分辨率,操作人:"+username+";所查id:"+content);
} catch (Exception e) {
result.setRecordsTotal(0);
result.setData(list);
result.setError(e.getMessage());
logger.error("短视频查询分辨率错误!!",e);
}
// 最后再删除一次临时保存的视频
if(tmp != null){
for (String videoId : tmp) {
// 视频保存路径File.separator
//DigestUtils.md5Hex(videoId);
videoId=videoId.trim();
String path = courseFile + File.separator + videoId.hashCode() + ".mp4";
deleteFile(path);
}
}
return result;
}
public boolean deleteFile(String fileName) {
File file = new File(fileName);
// 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
if (file.exists() && file.isFile()) {
if (file.delete()) {
logger.info("删除单个文件" + fileName + "成功!");
return true;
} else {
logger.info("删除单个文件" + fileName + "失败!");
return false;
}
} else {
logger.info("删除单个文件失败:" + fileName + "不存在!");
return false;
}
}
@RequestMapping("/img.do")
@ResponseBody
public CommonVO<?> imgClick(String videoId){
CommonVO<String> result = new CommonVO<String>();
try {
GetVideoPlayUrlReq paramGetVideoPlayUrlReq = new GetVideoPlayUrlReq();
paramGetVideoPlayUrlReq.setVideoId(videoId);
CommonResp<String> resp = backendVideoFacade.queryVideoPlayUrl(paramGetVideoPlayUrlReq);
if (resp != null && resp.getRetcode() == 0) {
result.setData(resp.getData());
return result;
}else {
result.setRetcode(9999);
result.setMessage("视频播放地址失效!!");
return result;
}
} catch (Exception e) {
e.printStackTrace();
result.setRetcode(9999);
result.setMessage("播放异常!!");
}
return result;
}
}