1. 程式人生 > >小程序短視頻項目———ffmpeg

小程序短視頻項目———ffmpeg

this java second stat string 處理 readline amr arraylist

視音頻處理工具

技術分享圖片

技術分享圖片

二、ffmpeg與java的結合

首先在com.imooc.utils新建FFMpegTest類

package com.imooc.utils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class FFMpegTest {

    private String ffmpegEXE;
    
    
    
public FFMpegTest(String ffmpegEXE) { super(); this.ffmpegEXE = ffmpegEXE; } public void convertor(String videoInputPath, String videoOutputPath) throws Exception { // ffmpeg -i input.mp4 output.avi /* * java調用cmd命令 */ List<String> command = new
ArrayList<>(); command.add(ffmpegEXE); command.add("-i"); command.add(videoInputPath); command.add(videoOutputPath); for(String c : command) { System.out.print(c); } ProcessBuilder builder = new ProcessBuilder(command); Process process
= builder.start(); InputStream errorStream = process.getErrorStream(); InputStreamReader inputStreamReader = new InputStreamReader(errorStream); BufferedReader br = new BufferedReader(inputStreamReader); String line = ""; while ( (line = br.readLine()) != null ) { } if (br != null) { br.close(); } if (inputStreamReader != null) { inputStreamReader.close(); } if (errorStream != null) { errorStream.close(); } } public static void main(String[] args) { FFMpegTest ffmpeg = new FFMpegTest("D:\\ffmpeg\\bin\\ffmpeg.exe"); try { ffmpeg.convertor("C:\\Users\\Administrator\\Pictures\\單挑.mp4", "C:\\Users\\Administrator\\Pictures\\鬥牛.avi"); } catch (Exception e) { e.printStackTrace(); } } }

三、java合並視音頻

MergeVideoMp3.class
package com.imooc.utils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class MergeVideoMp3 {

    private String ffmpegEXE;
    
    public MergeVideoMp3(String ffmpegEXE) {
        super();
        this.ffmpegEXE = ffmpegEXE;
    }
    
    public void convertor(String videoInputPath, String mp3InputPath,
            double seconds, String videoOutputPath) throws Exception {
//        ffmpeg.exe -i 蘇州大褲衩.mp4 -i bgm.mp3 -t 7 -y 新的視頻.mp4
        List<String> command = new ArrayList<>();
        command.add(ffmpegEXE);
        
        command.add("-i");
        command.add(videoInputPath);
        
        command.add("-i");
        command.add(mp3InputPath);
        
        command.add("-t");
        command.add(String.valueOf(seconds));
        
        command.add("-y");
        command.add(videoOutputPath);
        
//        for (String c : command) {
//            System.out.print(c + " ");
//        }
        
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = builder.start();
        
        InputStream errorStream = process.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        
        String line = "";
        while ( (line = br.readLine()) != null ) {
        }
        
        if (br != null) {
            br.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (errorStream != null) {
            errorStream.close();
        }
        
    }

    public static void main(String[] args) {
        MergeVideoMp3 ffmpeg = new MergeVideoMp3("D:\\ffmpeg\\bin\\ffmpeg.exe");
        try {
            ffmpeg.convertor("C:\\鬼.mp4", "C:\\music.mp3", 7.1, "C:\\這是通過java生產的視頻.mp4");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

package com.imooc.utils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class FFMpegTest {

    private String ffmpegEXE;
    
    public FFMpegTest(String ffmpegEXE) {
        super();
        this.ffmpegEXE = ffmpegEXE;
    }
    
    public void convertor(String videoInputPath, String videoOutputPath) throws Exception {
//        ffmpeg -i input.mp4 -y output.avi
        List<String> command = new ArrayList<>();
        command.add(ffmpegEXE);
        
        command.add("-i");
        command.add(videoInputPath);
        command.add("-y");
        command.add(videoOutputPath);
        
        for (String c : command) {
            System.out.print(c + " ");
        }
        
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = builder.start();
        
        InputStream errorStream = process.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        
        String line = "";
        while ( (line = br.readLine()) != null ) {
        }
        
        if (br != null) {
            br.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (errorStream != null) {
            errorStream.close();
        }
        
    }

    public static void main(String[] args) {
        FFMpegTest ffmpeg = new FFMpegTest("D:\\ffmpeg\\bin\\ffmpeg.exe");
        try {
            ffmpeg.convertor("C:\\鬼.mp4", "C:\\北京北京.avi");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

四、小程序上傳視頻後調用視頻處理工具聯調

技術分享圖片

五、保存視頻信息到數據庫

技術分享圖片

技術分享圖片

六、截圖(視頻封面)保存到數據庫中

1、後端接口的開發

    @ApiOperation(value="用戶上傳封面", notes="用戶上傳封面的接口")
    @ApiImplicitParams({
        @ApiImplicitParam(name="userId", value="用戶id", required=true, 
                dataType="String", paramType="form"),
        @ApiImplicitParam(name="videoId", value="視頻主鍵id", required=true, 
                dataType="String", paramType="form"),
    })
    @PostMapping(value="/uploadCover", headers="content-type=multipart/form-data")
    public IMoocJSONResult uploadCover(String userId,String videoId,
                        @ApiParam(value="短視頻", required=true)
                        MultipartFile file) throws Exception {  //Alt + shirt + R
        
        if (StringUtils.isBlank(videoId) || StringUtils.isBlank(userId)) {
            return IMoocJSONResult.errorMsg("視頻主鍵id和用戶id不能為空...");
        }
        
        //文件保存的空間
        String fileSpace = "D:/imooc_videos_dev";
        //保存到數據庫的相對路徑
        String uploadPathDB = "/" + userId + "/video" ;
        FileOutputStream fileOutputStream = null;
        InputStream inputStream = null;
        
        
        String finalCoverPath = "";
        try {
            if(file != null ) {
                
                
                String fileName = file.getOriginalFilename();
                if(StringUtils.isNoneBlank(fileName)) {
                    //文件上傳的最終路徑
                    finalCoverPath = fileSpace + uploadPathDB + "/" + fileName;
                    //設置數據庫保存的路徑
                    uploadPathDB += ("/" + fileName);
                    
                    File outFile = new File(finalCoverPath);
                    if(outFile.getParentFile() != null || !outFile.getParentFile().isDirectory()) {
                        
                        //創建父文件夾
                        outFile.getParentFile().mkdirs();
                    }
                    
                    fileOutputStream = new FileOutputStream(outFile);
                    inputStream = file.getInputStream();
                    IOUtils.copy(inputStream, fileOutputStream);
                    
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(fileOutputStream != null) {
                fileOutputStream.flush();
                fileOutputStream.close();
            }
        }
        
        videoService.updateVideo(videoId, uploadPathDB);

        
        return IMoocJSONResult.ok();
    
    
    }

2、前端js的開發

 upload: function(e) {
      var me = this;

      var bgmId = e.detail.value.bgmId;
      var desc = e.detail.value.desc;

      console.log("bgmId:" + bgmId);
      console.log("desc:" + desc);

      var duration = me.data.videoParams.duration;
      var tmpheight = me.data.videoParams.tmpHeight;
      var tmpwidth = me.data.videoParams.tmpWidth;
      var tmpVideoUrl = me.data.videoParams.tmpVideoUrl;
      var tmpCoverUrl = me.data.videoParams.tmpCoverUrl;

      //上傳短視頻
      wx.showLoading({
        title: ‘Loading...‘,
      })


      var serverUrl = app.serverUrl;
      wx.uploadFile({
        url: serverUrl + ‘/video/upload‘,
        
        formData: {
          userId: app.userInfo.id,    
          bgmId: bgmId,
          desc: desc,
          videoSeconds: duration,
          videoHeight: tmpheight,
          videoWidth: tmpwidth
        },
        
        filePath: tmpVideoUrl,
        name: ‘file‘,
        header: {
          ‘content-type‘: ‘application/json‘ // 默認值
        },
        success(res) {
          var data = JSON.parse(res.data);
          wx.hideLoading();
          if (data.status == 200) {

            var videoId = data.data;
            
            wx.showLoading({
              title: ‘上傳中...‘,
            })


            wx.uploadFile({
              url: serverUrl + ‘/video/uploadCover‘,

              formData: {
                userId: app.userInfo.id,
                videoId: videoId,
              },

              filePath: tmpCoverUrl,
              name: ‘file‘,
              header: {
                ‘content-type‘: ‘application/json‘ // 默認值
              },
              success(res) {
                var data = JSON.parse(res.data);
                wx.hideLoading();
                if (data.status == 200) {
                  wx.showToast({
                    title: ‘上傳成功!~~‘,
                    icon: "success"
                  });


                } else {
                  wx.showToast({
                    title: ‘上傳失敗!~~‘,
                    icon: "success"
                  })
                }

              }
            });
            wx.navigateBack({
              delta: 1,
            })
          } else {
            wx.showToast({
              title: ‘上傳失敗!~~‘,
              icon: "success"
            })
          } 

        }
      })
    }

在用手機端進行聯調時,上傳封面功能並不能實現,這是微信小程序的一個小坑。需要用ffmpeg去截視頻某一幀的圖才行。

七、使用ffmpeg生成截圖

技術分享圖片

生成截圖工具類

package com.imooc.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

/**
 * 
 * @Description: 獲取視頻的信息
 */
public class FetchVideoCover {
    // 視頻路徑
    private String ffmpegEXE;

    public void getCover(String videoInputPath, String coverOutputPath) throws IOException, InterruptedException {
//        ffmpeg.exe -ss 00:00:01 -i spring.mp4 -vframes 1 bb.jpg
        List<String> command = new java.util.ArrayList<String>();
        command.add(ffmpegEXE);
        
        // 指定截取第1秒
        command.add("-ss");
        command.add("00:00:06");
                
        command.add("-y");
        command.add("-i");
        command.add(videoInputPath);
        
        command.add("-vframes");
        command.add("1");
        
        command.add(coverOutputPath);
        
        for (String c : command) {
            System.out.print(c + " ");
        }
        
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = builder.start();
        
        InputStream errorStream = process.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        
        String line = "";
        while ( (line = br.readLine()) != null ) {
        }
        
        if (br != null) {
            br.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (errorStream != null) {
            errorStream.close();
        }
    }

    public String getFfmpegEXE() {
        return ffmpegEXE;
    }

    public void setFfmpegEXE(String ffmpegEXE) {
        this.ffmpegEXE = ffmpegEXE;
    }

    public FetchVideoCover() {
        super();
    }

    public FetchVideoCover(String ffmpegEXE) {
        this.ffmpegEXE = ffmpegEXE;
    }
    
    public static void main(String[] args) {
        // 獲取視頻信息。
        FetchVideoCover videoInfo = new FetchVideoCover("D:\\ffmpeg\\bin\\ffmpeg.exe");
        try {
            videoInfo.getCover("c:\\北京北京.avi","c:\\北京.jpg");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在controller補充對視頻的截圖

    @ApiOperation(value="用戶上傳視頻", notes="用戶上傳視頻的接口")
    @ApiImplicitParams({
        @ApiImplicitParam(name="userId", value="用戶id", required=true, 
                dataType="String", paramType="form"),
        @ApiImplicitParam(name="bgmId", value="背景音樂id", required=false, 
                dataType="String", paramType="form"),
        @ApiImplicitParam(name="videoSeconds", value="背景音樂播放長度", required=true, 
                dataType="String", paramType="form"),
        @ApiImplicitParam(name="videoWidth", value="視頻寬度", required=true, 
                dataType="String", paramType="form"),
        @ApiImplicitParam(name="videoHeight", value="視頻高度", required=true, 
                dataType="String", paramType="form"),
        @ApiImplicitParam(name="desc", value="視頻描述", required=false, 
                dataType="String", paramType="form")
    })
    @PostMapping(value="/upload", headers="content-type=multipart/form-data")
    public IMoocJSONResult upload(String userId, 
            String bgmId, double videoSeconds, 
            int videoWidth, int videoHeight,
            String desc,
            @ApiParam(value="短視頻", required=true)
            MultipartFile file) throws Exception {  //Alt + shirt + R
        
        if (StringUtils.isBlank(userId)) {
            return IMoocJSONResult.errorMsg("用戶id不能為空...");
        }
        
        //文件保存的空間
        String fileSpace = "D:/imooc_videos_dev";
        //保存到數據庫的相對路徑
        String uploadPathDB = "/" + userId + "/video" ;
        String coverPathDB = "/" + userId + "/video";
        
        
        FileOutputStream fileOutputStream = null;
        InputStream inputStream = null;
        
        
        String finalVideoPath = "";
        try {
            if(file != null ) {
                
                
                String fileName = file.getOriginalFilename();
                
                String fileNamePrefix = fileName.split("\\.")[0];
                
                if(StringUtils.isNoneBlank(fileName)) {
                    //文件上傳的最終路徑
                    finalVideoPath = fileSpace + uploadPathDB + "/" + fileName;
                    //設置數據庫保存的路徑
                    uploadPathDB += ("/" + fileName);
                    coverPathDB = coverPathDB + "/" + fileNamePrefix + ".jpg";
                    
                    File outFile = new File(finalVideoPath);
                    if(outFile.getParentFile() != null || !outFile.getParentFile().isDirectory()) {
                        
                        //創建父文件夾
                        outFile.getParentFile().mkdirs();
                    }
                    
                    fileOutputStream = new FileOutputStream(outFile);
                    inputStream = file.getInputStream();
                    IOUtils.copy(inputStream, fileOutputStream);
                    
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(fileOutputStream != null) {
                fileOutputStream.flush();
                fileOutputStream.close();
            }
        }
        
        //判斷bgmid是否為空,如果不為空,
        //那就查詢bgm的信息,並且合並視頻,生產新的視頻
        if (StringUtils.isNotBlank(bgmId)) {
            Bgm bgm = bgmService.queryBgmById(bgmId);
            String mp3InputPath = FILE_SPACE + bgm.getPath();
            
            MergeVideoMp3 tool = new MergeVideoMp3(FFMPEG_EXE);
            String videoInputPath = finalVideoPath;
            
            String videoOutputName = UUID.randomUUID().toString() + ".mp4";
            uploadPathDB = "/" + userId + "/video" + "/" + videoOutputName;
            finalVideoPath = FILE_SPACE + uploadPathDB;
            tool.convertor(videoInputPath, mp3InputPath, videoSeconds, finalVideoPath);
        }
        System.out.println("uploadPathDB=" + uploadPathDB);
        System.out.println("finalVideoPath=" + finalVideoPath);
        
        //對視頻進行截圖
        FetchVideoCover videoInfo = new FetchVideoCover(FFMPEG_EXE);
        videoInfo.getCover(finalVideoPath, FILE_SPACE + coverPathDB);
        
        //保存視頻信息到數據庫
        Videos video = new Videos();
        video.setAudioId(bgmId);
        video.setUserId(userId);
        video.setVideoSeconds((float)videoSeconds);
        video.setVideoHeight(videoHeight);
        video.setVideoWidth(videoWidth);
        video.setVideoDesc(desc);
        
        video.setVideoPath(uploadPathDB);
        video.setCoverPath(coverPathDB);
        video.setStatus(VideoStatusEnum.SUCCESS.value);
        video.setCreateTime(new Date());
        
        String videoId = videoService.saveVideo(video);
//        System.out.println("desc:"+desc);
        
        return IMoocJSONResult.ok(videoId);
    
    
    }

小程序短視頻項目———ffmpeg