阅读 511

SpringBoot 实现大文件视频转码(转码基于FFMPEG实现)

最近项目需要将用户上传的视频如果不是MP4格式,需要全部转码为MP4格式的,这里我通过FFMPeg进行大文件视频转码的实现。

1、安装FFMPeg

首先我们需要在机器上安装FFMPeg用于我们的视频转码,传送门如下:

Centos7 安装FFMPEG

Ubuntu 安装FFMPEG及简单使用

2、视频转码实现

package com.openailab.oascloud.file.common.helper;

import com.openailab.oascloud.file.common.config.BootstrapConfig;
import com.openailab.oascloud.file.common.consts.BootstrapConst;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * @description: 视频转码
 * @author: zhangzhixiang
 * @createDate: 2019/12/11
 * @version: 1.0
 */
@Component
public class FileManagementHelper {

    private static final Logger LOG = LoggerFactory.getLogger(FileManagementHelper.class);
    @Autowired
    private BootstrapConfig bootstrapConfig;
    /**
     * 文件转码
     *
     * @param filePath
     * @return com.openailab.oascloud.common.model.ResponseResult
     * @author zxzhang
     * @date 2019/12/10
     */
    public int transcode(String filePath) {
        //1、获取文件名和后缀
        String ext = getExt(filePath);
        String fileDir = getFileDir(filePath);
        String fileName = getFileName(filePath);
        //2、删除同名文件
        File originFile = new File(fileDir + fileName + BootstrapConst.SPOT + ext);
        if (originFile.exists()){
            originFile.delete();
        }
        //3、视频转码
        LinkedList<String> ffmpegCmds = new LinkedList<>();
        ffmpegCmds.add("ffmpeg");
        ffmpegCmds.add("-i");
        ffmpegCmds.add(bootstrapConfig.getFileRoot() + filePath);
        ffmpegCmds.add("-c:v");
        ffmpegCmds.add("libx264");
        ffmpegCmds.add("-strict");
        ffmpegCmds.add("-2");
        ffmpegCmds.add(bootstrapConfig.getFileRoot() + fileDir + BootstrapConst.PATH_SEPARATOR + fileName + ".mp4");
        ProcessBuilder builder = new ProcessBuilder();
        builder.command(ffmpegCmds);

        Process ffmpeg = null;
        try {
            ffmpeg = builder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String cmdStr = Arrays.toString(ffmpegCmds.toArray()).replace(",", "");
        LOG.info("---开始执行FFmpeg指令:--- 执行线程名:" + builder.toString());
        LOG.info("---已执行的FFmepg命令:---" + cmdStr);
        // 取出输出流和错误流的信息
        // 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,如果不取的话当输出流信息填满jvm存储输出留信息的缓冲区时,线程就回阻塞住
        PrintStream errorStream = new PrintStream(ffmpeg.getErrorStream());
        PrintStream inputStream = new PrintStream(ffmpeg.getInputStream());
        errorStream.start();
        inputStream.start();
        // 等待ffmpeg命令执行完
        int exit = 0;
        try {
            exit = ffmpeg.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        LOG.info("---执行结果:---" + (exit == 0 ? "【成功】" : "【失败】"));
        if (exit == 0) {
            originFile = new File(filePath);
            if(originFile.exists()){
                originFile.delete();
            }
        }
        return exit;
    }

    /**
     * 获取文件后缀
     *
     * @param fileName
     * @return java.lang.String
     * @author zxzhang
     * @date 2019/12/10
     */
    public String getExt(String fileName) {
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    /**
     * 获取文件所在目录
     *
     * @param filePath
     * @return java.lang.String
     * @author zxzhang
     * @date 2019/12/10
     */
    public String getFileDir(String filePath) {
        return filePath.substring(0, filePath.lastIndexOf(BootstrapConst.PATH_SEPARATOR));
    }

    /**
     * 获取文件名
     *
     * @param filePath
     * @return java.lang.String
     * @author zxzhang
     * @date 2019/12/10
     */
    public String getFileName(String filePath) {
        return filePath.substring(filePath.lastIndexOf(BootstrapConst.PATH_SEPARATOR) + 1, filePath.lastIndexOf("."));
    }
}

class PrintStream extends Thread {
    java.io.InputStream __is = null;

    public PrintStream(java.io.InputStream is) {
        __is = is;
    }

    @Override
    public void run() {
        try {
            while (this != null) {
                int _ch = __is.read();
                if (_ch != -1) {
                    System.out.print((char) _ch);
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码

3、FFMPeg DockerFile实现

首先我们构建具有JDK1.8和FFMPeg环境的镜像

FROM jrottenberg/ffmpeg:centos
MAINTAINER oas.cloud
ADD jdk-8u231-linux-x64.tar.gz /usr/local/
ENV JAVA_HOME /usr/local/jdk1.8.0_231
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin
WORKDIR /usr/local/oas/
复制代码

然后将上述文件构建出的镜像的tag打为我们自己的镜像仓库地址,改为 10.12.1.202:8088/oascloud/ffmpeg:v1.0,我们的应用程序就可以以上述镜像文件(10.12.1.202:8088/oascloud/ffmpeg:v1.0)为基础构建出新的应用程序镜像,应用程序的DockerFile如下:

FROM 10.12.1.202:8088/oascloud/ffmpeg:v1.0
MAINTAINER oas.cloud
ENV LC_ALL=zh_CN.utf8
ENV LANG=zh_CN.utf8
ENV LANGUAGE=zh_CN.utf8
RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
ARG JAR_FILE
COPY ${JAR_FILE} /usr/local/oas/
WORKDIR /usr/local/oas/
复制代码

代码结构如下:

  

附上Kubernetes编排文件,deploy.yaml内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: openailab-data-file-management
  namespace: oas-tengine2dev
  labels:
    name: openailab-data-file-management
spec:
  replicas: 1
  selector:
    matchLabels:
      name: openailab-data-file-management
  template:
    metadata:
      labels:
        name: openailab-data-file-management
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: env
                operator: In
                values:
                - tengine2dev
      containers:
      - name: openailab-data-file-management
        image: 10.12.1.202:8088/tengine2/openailab-data-file-management:latest
        env:
        - name: APOLLO_APP_ID
          value: 'tengine2-data-file-management'
        - name: APOLLO_EUREKA_URI
          value: 'http://10.98.100.95:8080/'
        - name: APOLLO_NAMESPACE
          value: 'application'
        - name: SPRING_EUREKA_URI
          value: 'http://10.111.176.222:8080/eureka/'
        volumeMounts:
        - name: openailab-data-file-management
          mountPath: /usr/local/logs
        - name: file
          mountPath: /usr/local/oas/file
        command: ['sh','-c','java -server -Xms2048m -Xmx3072m -Djava.io.tmpdir=/var/tmp -Duser.timezone=Asia/Shanghai -jar openailab-data-file-management-*.jar --server.port=8080 --spring.profiles.active=remote']
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: 256Mi
            cpu: 150m
          limits:
            memory: 2048Mi
            cpu: 300m
      initContainers:
      - name: init-apollo
        image: busybox
        command: ['sh', '-c']
        args:
        - echo Prepare to apollo initialize..;
          until nc -w 1 -z 10.98.100.95 8080;
          do echo Waiting for eureka initialization to complete..; sleep 10; done;
          echo Eureka is ready.;
          until nc -w 1 -z 10.98.100.95 8090;
          do echo Waiting for admin initialization to complete..; sleep 10; done;
          echo Admin is ready.;
          echo Apollo Initialization successful!;
      volumes:
      - name: openailab-data-file-management
        hostPath:
          path: /usr/local/logs
      - name: file
        hostPath:
          path: /usr/local/oas/file
复制代码

svc.yaml内容如下:

apiVersion: v1
kind: Service
metadata:
  name: openailab-data-file-management
  namespace: oas-tengine2dev
  labels:
    name: openailab-data-file-management
spec:
  type: NodePort
  ports:
  - nodePort: 32318
    targetPort: 8080
    port: 8080
    protocol: TCP
  selector:
    name: openailab-data-file-management
复制代码

然后我们就可以快快乐乐的通过Kubenetes编排文件的方式部署了。

注:如果只是通过Docker运行镜像(不建议),则需要修改DockerFile内容,修改后的DockerFile如下所示:

FROM 10.12.1.202:8088/oascloud/ffmpeg:v1.0
MAINTAINER oas.cloud
ENV LC_ALL=zh_CN.utf8
ENV LANG=zh_CN.utf8
ENV LANGUAGE=zh_CN.utf8
RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
ARG JAR_FILE
COPY ${JAR_FILE} /usr/local/oas/
WORKDIR /usr/local/oas/
CMD java -server -Xms2048m -Xmx3072m -Djava.io.tmpdir=/var/tmp -Duser.timezone=Asia/Shanghai -jar openailab-data-file-management-*.jar --server.port=32318
EXPOSE 32318
复制代码

到此 SpringBoot 实现大文件视频转码(转码基于FFMPEG实现)介绍完成。

文章分类
后端
文章标签