1、处理方式:
用户上传原始视频,系统对用户上传的视频自动编码、转换,最终生成m3u8
文件和
ts
文件,处理流程如下:
1
、用户上传视频成功
2
、系统对上传成功的视频自动开始编码处理
3
、用户查看视频处理结果,没有处理成功的视频用户可在管理界面再次触发处理
4
、视频处理完成将视频地址及处理结果保存到数据库
![](https://img-blog.csdnimg.cn/20191113155948380.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5Njg5NjA1,size_16,color_FFFFFF,t_70)
2、创建处理视频的springboot工程: test-media-processor
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
application.yml文件
server:
port: 91000
spring:
application:
name: test-media-processor
data:
mongodb:
uri: mongodb://root:root@localhost:27017
database: test_media
#rabbitmq配置
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
test-media:
mq:
queue-media-video-processor: queue_media_video_processor
routingkey-media-video: routingkey_media_video
video-location: F:/develop/video/
ffmpeg-path: F:/ffmpeg-20180227-fa0c9d6-win64-static/ffmpeg-20180227-fa0c9d6-win64-static/bin/ffmpeg.exe
3、MQ配置:
4、RabbitMQ配置如下:
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Administrator
* @version 1.0
* @create 2018-07-12 9:04
**/
@Configuration
public class RabbitMQConfig {
public static final String EX_MEDIA_PROCESSTASK = "ex_media_processor";
//视频处理队列
@Value("${test-media.mq.queue-media-video-processor}")
public String queue_media_video_processtask;
//视频处理路由
@Value("${test-media.mq.routingkey-media-video}")
public String routingkey_media_video;
/**
* 交换机配置
* @return the exchange
*/
@Bean(EX_MEDIA_PROCESSTASK)
public Exchange EX_MEDIA_VIDEOTASK() {
return ExchangeBuilder.directExchange(EX_MEDIA_PROCESSTASK).durable(true).build();
}
//声明队列
@Bean("queue_media_video_processtask")
public Queue QUEUE_PROCESSTASK() {
Queue queue = new Queue(queue_media_video_processtask,true,false,true);
return queue;
}
/**
* 绑定队列到交换机 .
* @param queue the queue
* @param exchange the exchange
* @return the binding
*/
@Bean
public Binding binding_queue_media_processtask(@Qualifier("queue_media_video_processtask") Queue queue, @Qualifier(EX_MEDIA_PROCESSTASK) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingkey_media_video).noargs();
}
}
5、java中使用 ffmpeg
ffmpeg
是一个可行的视频处理程序,可以通过
Java
调用
ffmpeg.exe
完成视频处理。
在
java
中可以使用
Runtime
类和
Process Builder
类两种方式来执行外部程序,工作中至少掌握一种。
本项目使用
Process Builder
的方式来调用
ffmpeg
完成视频处理。
关于
Process Builder
的测试如下:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestProcessBuilder {
//使用processBuilder来调用第三方应用程序
@Test
public void testProcessBuilder() throws IOException {
//创建processBuilder对象
ProcessBuilder processBuilder = new ProcessBuilder();
//设置第三方应用程序的命令
// processBuilder.command("ping","127.0.0.1");
processBuilder.command("ipconfig");
//将标准输入流和错误流合并
processBuilder.redirectErrorStream(true);
//启动一个进程
Process process = processBuilder.start();
//通过标准输入流来拿到正常和错误的信息
InputStream inputStream = process.getInputStream();
//转成字符流
InputStreamReader reader = new InputStreamReader(inputStream,"gbk");
//缓冲
char[] chars = new char[1024];
int len = -1;
while ((len = reader.read(chars))!=-1){
String string = new String(chars,0,len);
System.out.println(string);
}
inputStream.close();
reader.close();
}
@Test
public void testFFmpeg() throws IOException {
//创建processBuilder对象
ProcessBuilder processBuilder = new ProcessBuilder();
//设置第三方应用程序的命令
List<String> command = new ArrayList<>();
command.add("D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe");
command.add("-i");
command.add("E:\\ffmpeg_test\\1.avi");
command.add("-y");//覆盖输出文件
command.add("-c:v");
command.add("libx264");
command.add("-s");
command.add("1280x720");
command.add("-pix_fmt");
command.add("yuv420p");
command.add("-b:a");
command.add("63k");
command.add("-b:v");
command.add("753k");
command.add("-r");
command.add("18");
command.add("E:\\ffmpeg_test\\1.mp4");
processBuilder.command(command);
//将标准输入流和错误流合并
processBuilder.redirectErrorStream(true);
//启动一个进程
Process process = processBuilder.start();
//通过标准输入流来拿到正常和错误的信息
InputStream inputStream = process.getInputStream();
//转成字符流
InputStreamReader reader = new InputStreamReader(inputStream,"gbk");
//缓冲
char[] chars = new char[1024];
int len = -1;
while ((len = reader.read(chars))!=-1){
String string = new String(chars,0,len);
System.out.println(string);
}
inputStream.close();
reader.close();
}
@Test
public void testMp4VideoUtil(){
//String ffmpeg_path, String video_path, String mp4_name, String mp4folder_path
String ffmpeg_path = "D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";
String video_path = "E:\\ffmpeg_test\\1.avi";
String mp4_name = "1.mp4";
String mp4folder_path = "E:\\ffmpeg_test\\";
Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4folder_path);
//生成mp4
String result = mp4VideoUtil.generateMp4();
System.out.println(result);
}
}
6、工具类
Mp4VideoUtil.java
完成
avi
转
mp4
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Mp4VideoUtil extends VideoUtil {
String ffmpeg_path = "D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";//ffmpeg的安装位置
String video_path = "D:\\BaiduNetdiskDownload\\test1.avi";
String mp4_name = "test1.mp4";
String mp4folder_path = "D:/BaiduNetdiskDownload/Movies/test1/";
public Mp4VideoUtil(String ffmpeg_path, String video_path, String mp4_name, String mp4folder_path){
super(ffmpeg_path);
this.ffmpeg_path = ffmpeg_path;
this.video_path = video_path;
this.mp4_name = mp4_name;
this.mp4folder_path = mp4folder_path;
}
//清除已生成的mp4
private void clear_mp4(String mp4_path){
//删除原来已经生成的m3u8及ts文件
File mp4File = new File(mp4_path);
if(mp4File.exists() && mp4File.isFile()){
mp4File.delete();
}
}
/**
* 视频编码,生成mp4文件
* @return 成功返回success,失败返回控制台日志
*/
public String generateMp4(){
//清除已生成的mp4
clear_mp4(mp4folder_path+mp4_name);
/*
ffmpeg.exe -i lucene.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 .\lucene.mp4
*/
List<String> commend = new ArrayList<String>();
//commend.add("D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe");
commend.add(ffmpeg_path);
commend.add("-i");
// commend.add("D:\\BaiduNetdiskDownload\\test1.avi");
commend.add(video_path);
commend.add("-c:v");
commend.add("libx264");
commend.add("-y");//覆盖输出文件
commend.add("-s");
commend.add("1280x720");
commend.add("-pix_fmt");
commend.add("yuv420p");
commend.add("-b:a");
commend.add("63k");
commend.add("-b:v");
commend.add("753k");
commend.add("-r");
commend.add("18");
commend.add(mp4folder_path + mp4_name );
String outstring = null;
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
//将标准输入流和错误输入流合并,通过标准输入流程读取信息
builder.redirectErrorStream(true);
Process p = builder.start();
outstring = waitFor(p);
} catch (Exception ex) {
ex.printStackTrace();
}
Boolean check_video_time = this.check_video_time(video_path, mp4folder_path + mp4_name);
if(!check_video_time){
return outstring;
}else{
return "success";
}
}
public static void main(String[] args) throws IOException {
String ffmpeg_path = "D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";//ffmpeg的安装位置
String video_path = "E:\\ffmpeg_test\\1.avi";
String mp4_name = "809694a6a974c35e3a36f36850837d7c.mp4";
String mp4_path = "F:/develop/upload/8/0/809694a6a974c35e3a36f36850837d7c/";
Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4_path);
String s = videoUtil.generateMp4();
System.out.println(s);
}
}
HlsVideoUtil.java
完成
mp4
转
hls
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 此文件用于视频文件处理,步骤如下:
* 1、生成mp4
* 2、生成m3u8
*
*/
public class HlsVideoUtil extends VideoUtil {
String ffmpeg_path = "D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";//ffmpeg的安装位置
String video_path = "D:\\BaiduNetdiskDownload\\test1.avi";
String m3u8_name = "test1.m3u8";
String m3u8folder_path = "D:/BaiduNetdiskDownload/Movies/test1/";
public HlsVideoUtil(String ffmpeg_path, String video_path, String m3u8_name,String m3u8folder_path){
super(ffmpeg_path);
this.ffmpeg_path = ffmpeg_path;
this.video_path = video_path;
this.m3u8_name = m3u8_name;
this.m3u8folder_path = m3u8folder_path;
}
private void clear_m3u8(String m3u8_path){
//删除原来已经生成的m3u8及ts文件
File m3u8dir = new File(m3u8_path);
if(!m3u8dir.exists()){
m3u8dir.mkdirs();
}
/* if(m3u8dir.exists()&&m3u8_path.indexOf("/hls/")>=0){//在hls目录方可删除,以免错误删除
String[] children = m3u8dir.list();
//删除目录中的文件
for (int i = 0; i < children.length; i++) {
File file = new File(m3u8_path, children[i]);
file.delete();
}
}else{
m3u8dir.mkdirs();
}*/
}
/**
* 生成m3u8文件
* @return 成功则返回success,失败返回控制台日志
*/
public String generateM3u8(){
//清理m3u8文件目录
clear_m3u8(m3u8folder_path);
/*
ffmpeg -i lucene.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8
*/
// String m3u8_name = video_name.substring(0, video_name.lastIndexOf("."))+".m3u8";
List<String> commend = new ArrayList<String>();
commend.add(ffmpeg_path);
commend.add("-i");
commend.add(video_path);
commend.add("-hls_time");
commend.add("10");
commend.add("-hls_list_size");
commend.add("0");
commend.add("-hls_segment_filename");
// commend.add("D:/BaiduNetdiskDownload/Movies/test1/test1_%05d.ts");
commend.add(m3u8folder_path + m3u8_name.substring(0,m3u8_name.lastIndexOf(".")) + "_%05d.ts");
// commend.add("D:/BaiduNetdiskDownload/Movies/test1/test1.m3u8");
commend.add(m3u8folder_path + m3u8_name );
String outstring = null;
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
//将标准输入流和错误输入流合并,通过标准输入流程读取信息
builder.redirectErrorStream(true);
Process p = builder.start();
outstring = waitFor(p);
} catch (Exception ex) {
ex.printStackTrace();
}
//通过查看视频时长判断是否成功
Boolean check_video_time = check_video_time(video_path, m3u8folder_path + m3u8_name);
if(!check_video_time){
return outstring;
}
//通过查看m3u8列表判断是否成功
List<String> ts_list = get_ts_list();
if(ts_list == null){
return outstring;
}
return "success";
}
/**
* 检查视频处理是否完成
* @return ts列表
*/
public List<String> get_ts_list() {
// String m3u8_name = video_name.substring(0, video_name.lastIndexOf("."))+".m3u8";
List<String> fileList = new ArrayList<String>();
List<String> tsList = new ArrayList<String>();
String m3u8file_path =m3u8folder_path + m3u8_name;
BufferedReader br = null;
String str = null;
String bottomline = "";
try {
br = new BufferedReader(new FileReader(m3u8file_path));
while ((str = br.readLine()) != null) {
bottomline = str;
if(bottomline.endsWith(".ts")){
tsList.add(bottomline);
}
//System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (bottomline.contains("#EXT-X-ENDLIST")) {
// fileList.add(hls_relativepath+m3u8_name);
fileList.addAll(tsList);
return fileList;
}
return null;
}
public static void main(String[] args) throws IOException {
String ffmpeg_path = "D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";//ffmpeg的安装位置
String video_path = "E:\\ffmpeg_test\\1.mp4";
String m3u8_name = "1.m3u8";
String m3u8_path = "E:\\ffmpeg_test\\1\\";
HlsVideoUtil videoUtil = new HlsVideoUtil(ffmpeg_path,video_path,m3u8_name,m3u8_path);
String s = videoUtil.generateM3u8();
System.out.println(s);
System.out.println(videoUtil.get_ts_list());
}
}
VideoUtil
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 此文件作为视频文件处理父类,提供:
* 1、查看视频时长
* 2、校验两个视频的时长是否相等
*
*/
public class VideoUtil {
String ffmpeg_path = "D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";//ffmpeg的安装位置
public VideoUtil(String ffmpeg_path){
this.ffmpeg_path = ffmpeg_path;
}
//检查视频时间是否一致
public Boolean check_video_time(String source,String target) {
String source_time = get_video_time(source);
//取出时分秒
source_time = source_time.substring(0,source_time.lastIndexOf("."));
String target_time = get_video_time(target);
//取出时分秒
target_time = target_time.substring(0,target_time.lastIndexOf("."));
if(source_time == null || target_time == null){
return false;
}
if(source_time.equals(target_time)){
return true;
}
return false;
}
//获取视频时间(时:分:秒:毫秒)
public String get_video_time(String video_path) {
/*
ffmpeg -i lucene.mp4
*/
List<String> commend = new ArrayList<String>();
commend.add(ffmpeg_path);
commend.add("-i");
commend.add(video_path);
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
//将标准输入流和错误输入流合并,通过标准输入流程读取信息
builder.redirectErrorStream(true);
Process p = builder.start();
String outstring = waitFor(p);
System.out.println(outstring);
int start = outstring.trim().indexOf("Duration: ");
if(start>=0){
int end = outstring.trim().indexOf(", start:");
if(end>=0){
String time = outstring.substring(start+10,end);
if(time!=null && !time.equals("")){
return time.trim();
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public String waitFor(Process p) {
InputStream in = null;
InputStream error = null;
String result = "error";
int exitValue = -1;
StringBuffer outputString = new StringBuffer();
try {
in = p.getInputStream();
error = p.getErrorStream();
boolean finished = false;
int maxRetry = 600;//每次休眠1秒,最长执行时间10分种
int retry = 0;
while (!finished) {
if (retry > maxRetry) {
return "error";
}
try {
while (in.available() > 0) {
Character c = new Character((char) in.read());
outputString.append(c);
System.out.print(c);
}
while (error.available() > 0) {
Character c = new Character((char) in.read());
outputString.append(c);
System.out.print(c);
}
//进程未结束时调用exitValue将抛出异常
exitValue = p.exitValue();
finished = true;
} catch (IllegalThreadStateException e) {
Thread.currentThread().sleep(1000);//休眠1秒
retry++;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
return outputString.toString();
}
public static void main(String[] args) throws IOException {
String ffmpeg_path = "F:\\ffmpeg-20180227-fa0c9d6-win64-static\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";//ffmpeg的安装位置
VideoUtil videoUtil = new VideoUtil(ffmpeg_path);
String video_time = videoUtil.get_video_time("F:\\ffmpeg_test\\test.avi");
System.out.println(video_time);
}
}
错误处理工具类
@Data
@ToString
public class MediaFileProcess {
//错误信息
private String errormsg;
}
@Data
@ToString
public class MediaFileProcess_m3u8 extends MediaFileProcess {
//ts列表
private List<String> tslist;
}
7、编写dao
import org.springframework.data.mongodb.repository.MongoRepository;
public interface MediaFileRepository extends MongoRepository<MediaFile,String> {
}
8、编写mq监听
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* @author Administrator
* @version 1.0
**/
@Component
public class MediaProcessTask {
@Value("${test-media.ffmpeg-path}")
String ffmpeg_path;
//上传文件根目录
@Value("${test-media.video-location}")
String serverPath;
@Autowired
MediaFileRepository mediaFileRepository;
//接收视频处理消息进行视频处理
@RabbitListener(queues="${test-media.mq.queue-media-video-processor}")
public void receiveMediaProcessTask(String msg){
//1、解析消息内容,得到mediaId
Map map = JSON.parseObject(msg, Map.class);
String mediaId = (String) map.get("mediaId");
//2、拿mediaId从数据库查询文件信息
Optional<MediaFile> optional = mediaFileRepository.findById(mediaId);
if(!optional.isPresent()){
return ;
}
MediaFile mediaFile = optional.get();
//文件类型
String fileType = mediaFile.getFileType();
if(!fileType.equals("avi")){
mediaFile.setProcessStatus("303004");//无需处理
mediaFileRepository.save(mediaFile);
return ;
}else{
//需要处理
mediaFile.setProcessStatus("303001");//处理中
mediaFileRepository.save(mediaFile);
}
//3、使用工具类将avi文件生成mp4
//String ffmpeg_path, String video_path, String mp4_name, String mp4folder_path
//要处理的视频文件的路径
String video_path = serverPath + mediaFile.getFilePath() + mediaFile.getFileName();
//生成的mp4的文件名称
String mp4_name = mediaFile.getFileId() + ".mp4";
//生成的mp4所在的路径
String mp4folder_path = serverPath + mediaFile.getFilePath();
//创建工具类对象
Mp4VideoUtil mp4VideoUtil =new Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4folder_path);
//进行处理
String result = mp4VideoUtil.generateMp4();
if(result == null || !result.equals("success")){
//处理失败
mediaFile.setProcessStatus("303003");
//定义mediaFileProcess_m3u8
MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
//记录失败原因
mediaFileProcess_m3u8.setErrormsg(result);
mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
mediaFileRepository.save(mediaFile);
return ;
}
//4、将mp4生成m3u8和ts文件
//String ffmpeg_path, String video_path, String m3u8_name,String m3u8folder_path
//mp4视频文件路径
String mp4_video_path = serverPath + mediaFile.getFilePath() + mp4_name;
//m3u8_name文件名称
String m3u8_name = mediaFile.getFileId() +".m3u8";
//m3u8文件所在目录
String m3u8folder_path = serverPath + mediaFile.getFilePath() + "hls/";
HlsVideoUtil hlsVideoUtil = new HlsVideoUtil(ffmpeg_path,mp4_video_path,m3u8_name,m3u8folder_path);
//生成m3u8和ts文件
String tsResult = hlsVideoUtil.generateM3u8();
if(tsResult == null || !tsResult.equals("success")){
//处理失败
mediaFile.setProcessStatus("303003");
//定义mediaFileProcess_m3u8
MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
//记录失败原因
mediaFileProcess_m3u8.setErrormsg(result);
mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
mediaFileRepository.save(mediaFile);
return ;
}
//处理成功
//获取ts文件列表
List<String> ts_list = hlsVideoUtil.get_ts_list();
mediaFile.setProcessStatus("303002");
//定义mediaFileProcess_m3u8
MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
mediaFileProcess_m3u8.setTslist(ts_list);
mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
//保存fileUrl(此url就是视频播放的相对路径)
String fileUrl =mediaFile.getFilePath() + "hls/"+m3u8_name;
mediaFile.setFileUrl(fileUrl);
mediaFileRepository.save(mediaFile);
}
}
9、mq并发处理:
配置
mq
的容器工厂参数,增加并发处理数量即可实现多线程处理监听队列,实现多线程处理消息。
1
、在
RabbitmqConfifig.java
中添加容器工厂配置:
//消费者并发数量
public static final int DEFAULT_CONCURRENT = 10;
@Bean("customContainerFactory")
public SimpleRabbitListenerContainerFactory containerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConcurrentConsumers(DEFAULT_CONCURRENT);
factory.setMaxConcurrentConsumers(DEFAULT_CONCURRENT);
configurer.configure(factory, connectionFactory);
return factory;
}
2
、在
@RabbitListener
注解中指定容器工厂
@RabbitListener(queues="${xc-service-manage-media.mq.queue-media-video-processor}",containerFactory = "customContainerFactory")