Skip to content

Commit

Permalink
媒资管理_上传视频
Browse files Browse the repository at this point in the history
  • Loading branch information
huangkangyuan committed May 30, 2019
1 parent 4dbb889 commit 29ccc51
Show file tree
Hide file tree
Showing 17 changed files with 1,027 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.xuecheng.api.media;

import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.model.response.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.multipart.MultipartFile;

@Api(value = "媒资管理接口",description = "媒资管理接口,提供文件上传、处理等接口")
public interface MediaUploadControllerApi {

//文件上传前的准备工作,校验文件是否存在
@ApiOperation("文件上传注册")
public ResponseResult register(String fileMd5,
String fileName,
Long fileSize,
String mimeType,
String fileExt);

@ApiOperation("校验分块文件是否存在")
public CheckChunkResult checkChunk(String fileMd5,
Integer chunk,
Integer chunkSize);

@ApiOperation("上传分块")
public ResponseResult uploadChunk(MultipartFile file,
String fileMd5,
Integer chunk);

@ApiOperation("合并分块")
public ResponseResult mergeChunks(String fileMd5,
String fileName,
Long fileSize,
String mimeType,
String fileExt);

}
53 changes: 53 additions & 0 deletions xc-service-manage-media-processor/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xc-framework-parent</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-framework-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>xc-service-manage-media-processor</artifactId>
<dependencies>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-model</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-utils</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<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>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.xuecheng.manage_media_process;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;


/**
* @author Administrator
* @version 1.0
* @create 2018-07-12 8:57
**/
@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.media")//扫描实体类
@ComponentScan(basePackages={"com.xuecheng.api"})//扫描接口
@ComponentScan(basePackages={"com.xuecheng.manage_media_process"})//扫描本项目下的所有类
@ComponentScan(basePackages={"com.xuecheng.framework"})//扫描common下的所有类
public class ManageMediaProcessorApplication {
public static void main(String[] args) {
SpringApplication.run(ManageMediaProcessorApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.xuecheng.manage_media_process.config;

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;

@Configuration
public class RabbitMQConfig {

public static final String EX_MEDIA_PROCESSTASK = "ex_media_processor";

//视频处理队列
@Value("${xc-service-manage-media.mq.queue-media-video-processor}")
public String queue_media_video_processtask;

//视频处理路由
@Value("${xc-service-manage-media.mq.routingkey-media-video}")
public String routingkey_media_video;

//消费者并发数量
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;
}

/**
* 交换机配置
* @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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.xuecheng.manage_media_process.dao;

import com.xuecheng.framework.domain.media.MediaFile;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface MediaFileRepository extends MongoRepository<MediaFile,String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.xuecheng.manage_media_process.mq;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.MediaFileProcess_m3u8;
import com.xuecheng.framework.utils.HlsVideoUtil;
import com.xuecheng.framework.utils.Mp4VideoUtil;
import com.xuecheng.manage_media_process.dao.MediaFileRepository;
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;

@Component
public class MediaProcessTask {

@Value("${xc-service-manage-media.ffmpeg-path}")
String ffmpeg_path;

//上传文件根目录
@Value("${xc-service-manage-media.video-location}")
String serverPath;

@Autowired
MediaFileRepository mediaFileRepository;

//接收视频处理消息进行视频处理
@RabbitListener(queues="${xc-service-manage-media.mq.queue-media-video-processor}",containerFactory = "customContainerFactory")
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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
server:
port: 31450
spring:
application:
name: xc-service-manage-media-processor
data:
mongodb:
uri: mongodb://root:root@localhost:27017
database: xc_media
#rabbitmq配置
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
xc-service-manage-media:
mq:
queue-media-video-processor: queue_media_video_processor
routingkey-media-video: routingkey_media_video
video-location: F:/WebStormProject/video/
ffmpeg-path: E:/ffmpeg/ffmpeg-20180227-fa0c9d6-win64-static/bin/ffmpeg.exe
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>

<configuration>
<!--定义日志文件的存储地址,使用绝对路径-->
<property name="LOG_HOME" value="F:/document/logs"/>

<!-- Console 输出设置 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>

<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<fileNamePattern>${LOG_HOME}/xc.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<!-- 异步输出 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE"/>
</appender>


<logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<root level="info">
<!--<appender-ref ref="ASYNC"/>-->
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Loading

0 comments on commit 29ccc51

Please sign in to comment.