小程序短视频项目———ffmpeg

视音频处理工具

二、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);

    }

原文地址:https://www.cnblogs.com/bozzzhdz/p/9743739.html

时间: 2024-10-28 19:32:54

小程序短视频项目———ffmpeg的相关文章

小程序短视频项目———开发用户信息之用户退出注销

在用户登陆成功或者注册成功之后,应该让用户跳转到个人信息页面.所以接下来进行个人信息功能的开发记载 一.用户个人信息界面的初始化 mine.wxml <view> <view class='container'> <block wx:if="{{isMe}}"> <image src="{{faceUrl}}" class="face" bindtap='changeFace'></image

小程序短视频项目———开发用户信息之查询用户信息

一.后端接口开发 1.UserController.query( ) 2.service以及impl 显示界面 二·.小程序个人信息展示联调 mine.js登录成功跳转到个人信息界面的时候,触发onLoad()事件 onLoad: function(params){ var me = this; var user = app.userInfo; wx.showLoading({ title: '请等待...', }); var serverUrl = app.serverUrl; // 调用后端

小程序短视频项目———上传短视频业务

一.用户选择视频 1.微信选中视频接口 wx.chooseVideo(Object object) 拍摄视频或从手机相册中选视频. 参数 Object object 属性 类型 默认值 是否必填 说明 支持版本 sourceType Array.<string> ['album', 'camera'] 否 视频选择的来源   compressed boolean true 否 是否压缩所选择的视频文件 >= 1.6.0 maxDuration number 60 否 拍摄视频最长拍摄时间

小程序短视频项目———开发用户登录注册(二)

一.Session之有状态会话与无状态会话基本概念 其实就是没用到缓存的,直接储存在主机内存上的一种session方法. 依靠redis的一套操作 Redis-session的好处 二.开发用户Redis-session 首先在com.imooc.common中新建工具类RedisOperator类,注意要加入@Component注解,因为他要作为组件注入到spring容器中. package com.imooc.utils; import java.util.Map; import java.

微信小程序开发视频+项目实战

视频课程包含: 微信小程序入门视频.pages页面启动顺序配置.App实战开发视频教程.demo真机演示视频集锦.开发视频教程.源码+PPT.微信小程序五部入门+实战视频 等等 共9G! 这里给大家按照一定思路整理了微信小程序视频和项目实战,涵盖微信小程序全部知识点. 本视频属于作者原创搜集整理!下载方式:翻阅到文章底部 目录 1.微信小程序入门视频 2.微信小程序开发视频教程 3.微信小程pages页面启动顺序配置 4.微信小程序demo真机演示视频集锦 5.微信小程序五部入门+实战视频 6.

小程序音视频背后的故事

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 转载,本文作者,rexchang(常青),腾讯视频云终端技术总监,2008 年毕业加入腾讯,一直从事客户端研发相关工作,先后参与过 PC QQ.手机QQ.QQ物联 等产品项目,目前在腾讯视频云团队负责音视频终端解决方案的优化和落地工作,帮助客户在可控的研发成本投入之下,获得业内一流的音视频解决方案,目前我们的产品线包括:互动直播.点播.短视频.实时视频通话,图像处理,AI 等等. 为方便大家消化,请参考本篇文章的思维导图 本篇文章的脉络

小程序音视频能力技术负责人解读“小程序直播”

策划 / LiveVideoStack 责编 / 包研 一夜之间,"小程序+直播"成为多媒体开发者热议的话题.从底层技术实现到接口开放程度,是否绑定腾讯云?价格体系?低延迟性能如何?......一连串的问题背后是开发者乃至整个生态对"小程序+直播"的关注.LiveVideoStack邀请到小程序音视频能力的技术负责人常青,就开发者关注的各种问题进行了解答.如果您还有新的问题,请在在文末留言或邮件至[email protected]. 另外,我们还发起了针对"

微信小程序开发视频

微信小程序开发视频: http://pan.baidu.com/s/1hsqJC5e 开发下载链接: https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html

小程序音视频功能的原理及应用

本文由云+社区发表 作者:常青 腾讯视频云是做什么的?腾讯视频云既不做数据库,也不做存储,也不做网络,我们只做音视频服务,也就是直播.点播.视频通话.这类面向B类客户的音视频PAAS业务. 今天主要是跟大家讲的是腾讯视频云在过去一年时间里跟小程序结合,看看怎么去把这样一个高技术含量的一个能力去跟小程序这样一个非常轻量级的平台发挥1+1>2的效应. 今天的话题分四部分,第一个是小程序音视频能拿来做什么,第二部分是将其内部是怎么做到的?第三就是讲腾讯视频云的音视频技术的一些技术细节:第四个是介绍一下