记录一次探索在线视频播放的收获

12 篇文章 1 订阅

前言

最近接到一个需求,用户需要在浏览器上播放上传的视频,然后想到以前学习的学成在线项目中的媒资管理模块有这部分需求,立马开动,

后台服务搭建

作为一个开发者,风骚的走位是必须滴。。。所以就萌发了使用一把gradle的想法。
后端模块使用的是springboot + gradle + jetty

使用gradle遇到的问题

  1. gradle 中怎么使用springboot
    在maven 中我们引入Springboot 需要在pom.xml中设置依赖即可

      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>
    

    但是在gradle 中我们需要在build.gradle 文件中设置如下

    plugins {
        id 'java'
        // 指定springboot的版本
        id 'org.springframework.boot' version '2.0.5.RELEASE'
        // 使用springboot的依赖管理
        id 'io.spring.dependency-management' version '1.0.7.RELEASE'
    }
    
    // 依赖
    dependencies {
        /**
         * implementation和api是取代之前的compile的,其中api和compile是一样的效果,
         * implementation有所不同,通过implementation依赖的库只能自己库本身访问,
         * 举个例子,A依赖B,B依赖C,如果B依赖C是使用的implementation依赖,
         * 那么在A中是访问不到C中的方法的,如果需要访问,请使用api依赖
         */
        implementation(
                'org.springframework.boot:spring-boot-dependencies:2.0.5.RELEASE'
                )
    }
    
    

    其实仔细来看 gradle 和maven 还是有很多共通之处的,毕竟gradle是吸取了maven和ant而设计出来的

  2. gradle 中怎么使用jetty 替代tomcat

    configurations {
        // 排除掉tomcat
        compile.exclude module: "spring-boot-starter-tomcat"
        providedRuntime
        // remove default logger
        all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }	
    
    implementation(
    		// ... 省略其他的
    		/**
             * 使用jetty替代tomcat
             * Jetty和Tomcat性能方面差异不大
             * Jetty可以同时处理大量连接而且可以长时间保持连接,适合于web聊天应用等等。
             * Jetty的架构简单,因此作为服务器,Jetty可以按需加载组件,减少不需要的组件,减少了服务器内存开销,从而提高服务器性能。
             * Jetty默认采用NIO结束在处理I/O请求上更占优势,在处理静态资源时,性能较高
             * 因为本功能主要是支持视频,图片的读取 所以采用jetty代替tomcat
             */
            'org.springframework.boot:spring-boot-starter-jetty',
    )
    
    
    
    

使用gradle之后的总结

  1. gradle 实在太灵活了,灵活到idea 没有代码提示。
  2. 构建太费劲了,最终还是屈服于gradleWrapper了。
  3. 每次debug 起项目的时候gradle会启动2个控制台 这一点很不能够理解
  4. gradle的编译速度和打包速度很快

后台实现思路

需求

1. 由于浏览器能够播放的视频格式是有限的,而用户上传的视频格式不确定的,所以我们需要对视频进行处理。
2. 针对于每一个视频,我们需要截图视频中的第一秒画面作为视频封面。
3. 针对于大视频,用户不能感受到明显的卡顿,应该边缓冲边播放。

流程图:

截取图片
再次检查类型
转换成为m3u8
用户上传的视频
jpg
最终文件
是否为mp4
进行文件类型转换
mp4
m3u8

后台代码

service.upload

    public MediaFileVo upload(MultipartFile file, String uid) throws Exception {
        // 1.保存上传记录
        MediaFilePo mediaFilePo = saveFileInfo(file);
        // 2.上传文件
        saveFileInServer(file, mediaFilePo);
        // 3.将文件转换格式 转换格式的过程另外起一个线程去做 使用socket往前台推送进度
        MediaFileVo vo = new MediaFileVo();
        BeanUtils.copyProperties(mediaFilePo, vo);
        executor.execute(() -> {
            CurrentConvertVideoSign.put(uid);
            try {
                MediaFileVo curVo = transform(mediaFilePo);
                // 4. 保存图片地址
                mediaFilePo.setPicPath(curVo.getPicPath());
                mediaFileRepository.save(mediaFilePo);
                ConvertProcessVo processVo = new ConvertProcessVo(ConvertProcessVo.END);
                processVo.setDesc("转码完成!");
                WebSocketServer.sendInfo(CurrentConvertVideoSign.get(), processVo);
            } catch (Exception e) {
                ConvertProcessVo processVo = new ConvertProcessVo(ConvertProcessVo.EXCEPTION);
                processVo.setDesc("转码过程中发生异常!");
                WebSocketServer.sendInfo(CurrentConvertVideoSign.get(), processVo);
            } finally {
                CurrentConvertVideoSign.clear();
            }
        });
        return vo;
    }
    /**
     * 此处实现文件的转换逻辑 在这里设定转换类型
     *
     * @param po
     * @return
     */
    public MediaFileVo transform(MediaFilePo po) throws Exception {
        MediaFileVo vo = new MediaFileVo();
        if (po != null) {
            BeanUtils.copyProperties(po, vo);
        }
        if (needTransform(vo)) {
            // 使用文件转换器进行转换
            return mediaFileTransverterManager.transform(vo);
        }
        return vo;
    }

MediaFileTransverterManager

/**
 * 转换管理器的本身也是文件转换器的一个实现
 */
@Component
public class MediaFileTransverterManager implements MediaFileTransverter {

    private Map<String, MediaFileTransverter> transverterMap = new HashMap<>();

    /**
     * 添加一个转换器
     *
     * @param sourceTypes 源文件类型
     * @param destType    目标类型
     * @param transverter 转换器对象
     */
    public void addTransverter(String[] sourceTypes, String destType, MediaFileTransverter transverter) {
        for (String sourceType : sourceTypes) {
            String customKey = sourceType +SPLIT+ destType;
            if (transverterMap.containsKey(customKey)) {
                throw new RuntimeException(String.format("%s is exist!old is %s ", customKey, transverterMap.get(customKey).toString()));
            }
            transverterMap.put(customKey, transverter);
        }
    }

    /**
     * 获取转换器
     * @param mediaFileVo
     * @return
     */
    public MediaFileTransverter getTransverter(MediaFileVo mediaFileVo){
        String convertType = mediaFileVo.getConvertType();
        if(StringUtils.isBlank(convertType)){
            // 如果转换前后的类型相同,或者没有指定需要转换的类型 那么视为不提供转换器
            return null;
        }
        return transverterMap.get(mediaFileVo.getFileType() +SPLIT+ convertType);
    }


    @Override
    public MediaFileVo transform(MediaFileVo mediaFileVo) throws Exception{
        MediaFileTransverter transverter = getTransverter(mediaFileVo);
        if(transverter != null){
            return transverter.transform(mediaFileVo);
        }
        return mediaFileVo;
    }
}

MediaFileTransverter

public interface MediaFileTransverter  extends MediaTypes, MediaConstants {

    /**
     * 将原本的媒资文件转换成为一个新的格式
     * 例如 avi 转换成为 mp4
     * @param mediaFileVo 当前的文件对象
     * @return
     */
    MediaFileVo transform(MediaFileVo mediaFileVo) throws Exception;

}

调用ffmpeg进行视频处理

在后台代码中比较麻烦的就是如何通过ffmpeg 对视频文件进行处理,之前在网上找了很多资料,他们的avi转成mp4之后视频画面都会出现绿色的图案,最终还是选择使用学成在线中的转换命令成功解决这个问题。

  1. 视频转换成为mp4格式
    ffmpeg.exe -i lucene.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 .\lucene.mp4

  2. 截取首页图片
    ffmpeg -ss 00:00:01 -i test1.flv -f image2 -y test1.jpg

  3. mp4格式转换成为m3u8格式
    ffmpeg -i lucene.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8

补充

  1. ffmpeg 支持的格式是有限的,对于其他格式很多方案是先通过mencoder对视频进行处理,然后在使用ffmpeg处理

前台项目搭建

为了贯彻要做就做全套的理念,后台搞了一点骚操作 前台必须也要跟上,所以前台框架选择了react + react-element

使用react 遇到的问题

  1. react-element 的资料很难找,最开始选框架很重要。
  2. 由于之前使用的项目都是vue ,使用习惯了v-if v-show 等指令,突然转换成为使用render 构建多少有些不适应。
  3. 打包发布的时候是空白屏
    a> 使用HashRouter 替换BrowserRouter,这一点类似与vue 中的routerd hash模式和history模式
    index.js
    	// 加入路由
    import {routes} from './router/index'
    import {HashRouter} from 'react-router-dom'
    import {renderRoutes} from 'react-router-config';
    ReactDOM.render(
        (<HashRouter>
            {renderRoutes(routes)}
        </HashRouter>),
        document.getElementById('root')
    );
    
    b> rouer/index.js 中需要设置严格模式,否则会不能通过路由进入具体的组件
    import app from './../App'
    import VideoPlayer from './../component/video/player/VideoPlayer'
    import VideoCardPage from './../component/video/VideoCardPage'
    
    const routes = [
        {
            path: '/',
            component: app,
            exact: true
        },
        {
            path: '/video/player/:id',
            component: VideoPlayer,
            exact: true
        },
        {
            path: '/video/cardPage',
            component: VideoCardPage,
            exact: true
        }
    ]
    
    export {routes}
    
    
    

效果图

在这里插入图片描述
文件上传中
在这里插入图片描述
开始截取封面
在这里插入图片描述
进行转码
在这里插入图片描述

总结

做技术 还是要折腾 ,虽然折腾的过程中很累,有句话说的好,付出不一定有回报,但是不付出一定没有,经历了这个项目,让我对视频流的处理有了一个简单的认识,了解到了gradle,react 这些以前没有用过的技术,奥里给!

代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值