FFmpeg视频处理

深藏阁楼爱情的钟 2023-10-04 22:32 30阅读 0赞

视频处理

1、**什么是视频编码**

视频上传成功后需要对视频进行转码处理。

什么是视频编码?查阅百度百科如下:

86baea538f284f6db314baa30a6e492d.png

详情参考 :百度百科-验证

首先我们要分清文件格式和编码格式:

文件格式:是指.mp4、.avi、.rmvb等 这些不同扩展名的视频文件的文件格式 ,视频文件的内容主要包括视频和音频,其文件格式是按照一 定的编码格式去编码,并且按照该文件所规定的封装格式将视频、音频、字幕等信息封装在一起,播放器会根据它们的封装格式去提取出编码,然后由播放器解码,最终播放音视频。

音视频编码格式:通过音视频的压缩技术,将视频格式转换成另一种视频格式,通过视频编码实现流媒体的传输。比如:一个.avi的视频文件原来的编码是a,通过编码后编码格式变为b,音频原来为c,通过编码后变为d。

音视频编码格式各类繁多,主要有几下几类:

1.1.MPEG系列

(由ISO[国际标准组织机构]下属的MPEG[运动图象专家组]开发 )视频编码方面主要是Mpeg1(vcd用的就是它)、Mpeg2(DVD使用)、Mpeg4(的DVDRIP使用的都是它的变种,如:divx,xvid等)、Mpeg4 AVC(正热门);音频编码方面主要是MPEG Audio Layer 1/2、MPEG Audio Layer 3(大名鼎鼎的mp3)、MPEG-2 AAC 、MPEG-4 AAC等等。注意:DVD音频没有采用Mpeg的。

1.1. H.26X系列

(由ITU[国际电传视讯联盟]主导,侧重网络传输,注意:只是视频编码)

包括H.261、H.262、H.263、H.263+、H.263++、H.264(就是MPEG4 AVC-合作的结晶)

目前最常用的编码标准是视频H.264,音频AAC。

提问:

H.264是编码格式还是文件格式?

mp4是编码格式还是文件格式?

2.FFmpeg 的基本使用

我们将视频录制完成后,使用视频编码软件对视频进行编码,本项目 使用FFmpeg对视频进行编码 。

aafd252d4ab1484c9f795035a6fb50e6.png

FFmpeg被许多开源项目采用,QQ影音、暴风影音、VLC等。

下载:FFmpeg Download FFmpeg

测试是否正常:cmd运行 ffmpeg -v

c032e9d7cd364da7ac9ed6a3946288cb.png

安装成功,作下简单测试

将一个.avi文件转成mp4、mp3、gif等。

比如我们将nacos.avi文件转成mp4,运行如下命令:

D:\soft\ffmpeg\ffmpeg.exe -i 1.avi 1.mp4

02f473584bb84fd9b560065db738f921.png

转码成功:2c6c280947de4909a7b70410aea1e5c5.png

cc2f6c7185564de8b6b43600de7e8bd7.png

可以将ffmpeg.exe配置到环境变量path中,进入视频目录直接运行:ffmpeg.exe -i 1.avi 1.mp4

转成mp3:ffmpeg -i nacos.avi nacos.mp3

转成gif:ffmpeg -i nacos.avi nacos.gif

官方文档(英文):ffmpeg Documentation

3. 视频处理工具类

将课程资料目录中的util.zip解压,将解压出的工具类拷贝至base工程。

e5e435dd58f0410486a0325e0db566ef.png

其中Mp4VideoUtil类是用于将视频转为mp4格式,是我们项目要使用的工具类。

下边看下这个类的代码,并进行测试。

我们要通过ffmpeg对视频转码,Java程序调用ffmpeg,使用java.lang.ProcessBuilder去完成,具体在Mp4VideoUtil类的63行,下边进行简单的测试,下边的代码运行本机安装的QQ软件。

3.1. java操作:







Java

  1. //启动QQ
    ProcessBuilder builder = new ProcessBuilder();
    builder.command(“D:\QQ\Bin\QQScLauncher.exe”);
    //将标准输入流和错误输入流合并,通过标准输入流程读取信息
    builder.redirectErrorStream(true);
    Process p = builder.start();

对Mp4VideoUtil类需要学习使用方法,下边代码将一个avi视频转为mp4视频,如下:







Java

  1. public static void main(String[] args) throws IOException {

    //转换文件
    //ffmpeg的路径
    String ffmpeg_path = D:\software\ffmpeg\ffmpeg.exe”;//ffmpeg的安装位置
    //源avi视频的路径
    String video_path = D:\software\Test\xuecheng\video\11.avi”;
    //转换后mp4文件的名称
    String mp4_name = 1.mp4”;
    //转换后mp4文件的路径
    String mp4_path = D:\software\Test\xuecheng\video\1.mp4”;
    //创建工具类对象
    Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpeg_path, video_path, mp4_name, mp4_path);
    //开始视频转换,成功将返回success
    String s = videoUtil.generateMp4();
    System.out.println(s);
    }

执行main方法,最终在控制台输出 success 表示执行成功。

4. 任务类

视频采用并发处理,每个视频使用一个线程去处理,每次处理的视频数量不要超过cpu核心数。

所有视频处理完成结束本次执行,为防止代码异常出现无限期等待则添加超时设置,到达超时时间还没有处理完成仍结束任务。

定义任务类VideoTask 如下:







Java

  1. package com.xuecheng.media.service.jobhander;

    import com.xuecheng.base.utils.Mp4VideoUtil;
    import com.xuecheng.media.model.po.MediaProcess;
    import com.xuecheng.media.service.MediaFileProcessService;
    import com.xuecheng.media.service.MediaFileService;
    import com.xxl.job.core.context.XxlJobHelper;
    import com.xxl.job.core.handler.annotation.XxlJob;
    import lombok.extern.slf4j.Slf4j;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;

    import java.io.File;
    import java.io.IOException;
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;


    /
    视频采用并发处理,每个视频使用一个线程去处理,每次处理的视频数量不要超过cpu核心数。
  2. 所有视频处理完成结束本次执行,为防止代码异常出现无限期等待则添加超时设置,到达超时时间还没有处理完成仍结束任务。
    */
    @Component
    @Slf4j
    public class VideoTask {

    @Autowired
    MediaFileProcessService mediaFileProcessService;

    @Autowired
    MediaFileService mediaFileService;

    @Value(“${videoprocess.ffmpegpath}”)
    String ffmpegpath;

    /

    视频处理任务
  3. /
    @XxlJob(“videoJobHander”)
    public void videoJobHander() throws Exception {

    // 分片参数
    int shardIndex = XxlJobHelper.getShardIndex();
    int shardTotal = XxlJobHelper.getShardTotal();

    //查询待处理任务,一次处理的任务数要和cup核心一样
    List<MediaProcess> mediaProcessList = mediaFileProcessService.getMediaProcessList(shardIndex, shardTotal, 2);

    if (mediaProcessList == null || mediaProcessList.size() <= 0) {
    log.debug(“查询到的待处理的视频为0”);
    return;
    }
    //要处理的任务数
    int size = mediaProcessList.size();

    //启动size个线程的线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(size);

    //计数器
    CountDownLatch countDownLatch = new CountDownLatch(size);


    //遍历mediaProcessList,将处理任务加入线程池
    mediaProcessList.forEach(mediaProcess -> {
    threadPool.execute(() -> {
    //视频处理状态
    String status = mediaProcess.getStatus();
    //保证幂等性
    if (“2”.equals(status)) {
    log.debug(“视频已经处理不用再次处理,视频信息:{}”, mediaProcessList);
    return;
    }
    //桶
    String bucket = mediaProcess.getBucket();
    //存储路径
    String filePath = mediaProcess.getFilePath();
    //原始视频的md5值
    String fileId = mediaProcess.getFileId();
    //原始文件名称
    String filename = mediaProcess.getFilename();

    //将要处理的文件下载到服务器上
    File originalFile = null;
    //处理结束的视频文件
    File mp4File = null;

    try {
    originalFile = File.createTempFile(“original”, null);
    mp4File = File.createTempFile(“mp4”, “.mp4”);
    } catch (IOException e) {
    log.error(“处理视频前创建临时文件失败”);
    countDownLatch.countDown();//计数器减1
    return;
    }

    try {
    //将原始视频下载到本地
    mediaFileService.downloadFileFromMinIO(originalFile, bucket, filePath);
    } catch (Exception e) {
    log.error(“下载源始文件过程出错:{},文件信息:{}”, e.getMessage(), mediaProcess);
    countDownLatch.countDown();//计数器减1
    return;
    }

    //调用工具类将avi转成mp4

    //转换后mp4文件的名称
    String mp4_name = fileId + “.mp4”;
    //转换后mp4文件的路径
    String mp4_path = mp4File.getAbsolutePath();
    //创建工具类对象
    Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpegpath, originalFile.getAbsolutePath(), mp4_name, mp4_path);
    //开始视频转换,成功将返回success
    String result = videoUtil.generateMp4();
    String statusNew = 3”;
    String url = null; // 最终访问路径
    if (“success”.equals(result)) {
    //转换成功
    //上传到minion的路径
    String objectName = getFilePath(fileId, “.mp4”);
    try {
    //上传到minion
    mediaFileService.addMediaFilesToMinIO(mp4_path, bucket, objectName);
    } catch (Exception e) {
    log.debug(“上传文件出错:{}”, e.getMessage());
    countDownLatch.countDown();//计数器减1
    return;
    }
    statusNew = 2”; //处理成功
    url = “/“ + bucket + “/“ + objectName;
    }


    try {
    //记录任务处理的结果
    mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(), statusNew, fileId, url, result);
    } catch (Exception e) {
    log.debug(“保存任务处理结果出错:{}”, e.getMessage());
    countDownLatch.countDown();//计数器减1
    return;
    }
    //计数器减去1
    countDownLatch.countDown();
    });
    });

    //阻塞到任务执行完成,当countDownLatch计数器归零,这里的阻塞解除
    //等待,给一个充裕的超时时间,防止无限等待,到达超时时间还没有处理完成则结束任务
    countDownLatch.await(30, TimeUnit.MINUTES);


    }

    //生成路径
    private String getFilePath(String fileMd5, String fileExt) {
    return fileMd5.substring(0, 1) + “/“ + fileMd5.substring(1, 2) + “/“ + fileMd5 + “/“ + fileMd5 + fileExt;
    }


    }

发表评论

表情:
评论列表 (有 0 条评论,30人围观)

还没有评论,来说两句吧...

相关阅读