JAVA调用FFMpeg进行转码等操作

简介: JAVA调用FFMpeg进行转码等操作

直接上代码:

public abstract class FFmpegUtils {

FFmpegUtils ffmpegUtils;

int timeLengthSec = 1;

String timeLength = "";

Pattern pattern = Pattern.compile("Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s");
String frameRegexDuration = "size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x";
String videoframeRegexDuration = "frame=([\\s,\\d]*) fps=(.*?) q=(.*?) size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x";
Pattern framePattern = Pattern.compile(frameRegexDuration);

public static void main(String[] args){
    String target = "";

/* try {

        target = extractAsyn("D:\\ffmpeg4.2\\bin\\ffmpeg.exe", 
                "-y   -f   image2   -ss   1   -t   0.001   -s   640x480", 
                "E:\\迅雷下载\\电影\\test.avi", 
                "E:\\迅雷下载\\电影\\test.avi.jpg");
        System.out.println(target);
    } catch (Throwable e) {
        System.err.println(e.getMessage());
    }
    */
    try {
        
         new FFmpegUtils() {
            @Override
            public void dealLine(String line) {
                System.out.println(line);
                if(timeLength == null || timeLength.equals("")) {
                    Matcher m = pattern.matcher(line.trim());
                    if (m.find()) {
                        timeLength = m.group(1);
                        if(timeLength!=null){
                            timeLengthSec = FFVideoUtil.getTimelen(timeLength);
                        }
                        System.out.println(timeLength+"||"+timeLengthSec);
                    }
                }

                //获取视频信息
                Matcher matcher = framePattern.matcher(line);
                if(matcher.find()){
                    try {
                        String execTimeStr = matcher.group(2);
                        int execTimeInt = FFVideoUtil.getTimelen(execTimeStr);
                        double devnum = FFBigDecimalUtil.div(execTimeInt,timeLengthSec,5);
                        double progressDouble = FFBigDecimalUtil.mul(devnum,100);
                        System.out.println("execTimeInt:"+execTimeInt+"&,devnum:"+devnum+"&,progressDouble:"+progressDouble);
                    } catch (IllegalAccessException e) {
                        System.err.println("获取输出流异常:"+e.getMessage());
                    }
                }
            }
            
            @Override
            public void dealStream(Process process) {
                 if (process == null) {
                        return;
                    }
                    // 处理InputStream的线程
                    new Thread() {
                        @Override
                        public void run() {
                            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
                            String line = null;
                            try {
                                while ((line = in.readLine()) != null) {
                                    //logger.info("output: " + line);
                                    dealLine(line);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            } finally {
                                try {
                                    in.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }.start();
                    // 处理ErrorStream的线程
                    new Thread() {
                        @Override
                        public void run() {
                            BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                            String line = null;
                            try {
                                while ((line = err.readLine()) != null) {
                                    dealLine(line);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            } finally {
                                try {
                                    err.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }.start();
            }
        }.processVideoSync("D:\\ffmpeg4.2\\bin\\ffmpeg.exe", 
                " -f|mp3", 
                "E:\\迅雷下载\\电影\\test.avi", 
                "E:\\迅雷下载\\电影\\test.avi.mp3");
        System.out.println(target);
    } catch (Throwable e) {
        System.err.println(e.getMessage());
    }
    
}

//异步 适合抽帧等快速的操作
public static String extractAsyn(
        String ffmpegPath,String cmdParam,
        String sourceFile,String targetFile) 
                throws Throwable {
        
        Runtime runtime = Runtime.getRuntime();
        Process proce = null;
        // 视频截图命令,封面图。 8是代表第8秒的时候截图
        String cmd = "";
        String cut = ffmpegPath +" -i "+ sourceFile +"  "+ cmdParam +"  "+ targetFile;
        String cutCmd = cmd + cut;
        proce = runtime.exec(cutCmd);
        proce.getOutputStream();
        System.out.println("抽帧命令是:"+cut);
        return targetFile;
}


public static boolean checkfile(String path) {
    File file = new File(path);
    if (!file.isFile()) {
        return false;
    }
    return true;
}

//异步处理
public boolean processVideoSync(String ffmpegPath,String cmdParam,
        String sourceFile,String targetFile) {

    // 文件命名
    List<String> commond = new ArrayList<String>();
    commond.add(ffmpegPath);
    commond.add("-i");
    commond.add(sourceFile);
    commond.addAll(Arrays.asList(cmdParam.trim().split("\\|")));
    commond.add(targetFile);
    
    if(new File(targetFile).exists()) {
        new File(targetFile).delete();
    }
    
    String cmds = ""; 
    for (String cmd : commond) {
        cmds = cmds + " " + cmd;
    }
    System.out.println("执行命令参数为:" + cmds);
    try {
        // 调用线程命令进行转码
        Process videoProcess = new ProcessBuilder(commond).redirectErrorStream(true).start();
        //new PrintStream(videoProcess.getInputStream()).start();          
        //videoProcess.waitFor();
        /*new InputStreamReader(videoProcess.getErrorStream());
        BufferedReader stdout = new BufferedReader(new InputStreamReader(videoProcess.getInputStream()));
        String line;
        while ((line = stdout.readLine()) != null) {
             dealLine(line);
        }*/
        dealStream(videoProcess);
        videoProcess.waitFor();
        
        
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

//处理输出流
public abstract void dealLine(String line);
public abstract void dealStream(Process process );

}
FFmpegUtils.java
@Component
public class ProgressService extends FFmpegUtils{

public static Logger logger = LoggerFactory.getLogger(ProgressService.class);

 /**
 * 进度正则查询
 */
private String frameRegexDuration = "frame=([\\s,\\d]*) fps=(.*?) q=(.*?) size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x";

/**
 * 正则模式
 */
private Pattern framePattern = Pattern.compile(frameRegexDuration);

/**
 * 秒数
 */
private Integer timeLengthSec;

/**
 * 时长
 */
private String timeLength;

/**
 * 开始时间
 */
private String startTime;

/**
 * 比特率
 */
private String bitrate;

/**
 *  时长 正则
 */
private String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";

/**
 * 正则模式
 */
private Pattern pattern = Pattern.compile(regexDuration);

public String getStartTime() {
    return startTime;
}

public void setStartTime(String startTime) {
    this.startTime = startTime;
}

public String getBitrate() {
    return bitrate;
}

public void setBitrate(String bitrate) {
    this.bitrate = bitrate;
}



private TranscodeTask task;

public TranscodeTask getTask() {
    return task;
}

public void setTask(TranscodeTask task) {
    this.task = task;
}

@Autowired
private TaskReposity _taskRep;


@Override
public void dealLine(String line) {
    logger.debug("{}输出信息:{}",task.getName(),line);
    //获取视频长度信息
    if(timeLength == null || "".equals(timeLength)) {
        Matcher m = pattern.matcher(line.trim());
        if (m.find()) {
            timeLength = m.group(1);
            if(timeLength!=null){
                timeLengthSec = FFVideoUtil.getTimelen(timeLength);
            }
            startTime = m.group(2);
            bitrate = m.group(3);
            logger.debug("timeLength:{}, startTime:{},bitrate:{}",timeLength,startTime,bitrate);
        }
    }
    
     //获取视频信息
    Matcher matcher = framePattern.matcher(line);
    if(matcher.find()){
        try {
            String execTimeStr = matcher.group(5);
            int execTimeInt = FFVideoUtil.getTimelen(execTimeStr);
            double devnum = FFBigDecimalUtil.div(execTimeInt,timeLengthSec,5);
            double progressDouble = FFBigDecimalUtil.mul(devnum,100);
            logger.debug("execTimeInt:{},devnum:{},progressDouble:{}",execTimeInt,devnum,progressDouble);
            task.setProgress((float)progressDouble);
            _taskRep.saveAndFlush(this.task);
        } catch (IllegalAccessException e) {
            logger.error("获取输出流异常:{}",e.getMessage());
        }
    }
}

/**
 * 处理process输出流和错误流,防止进程阻塞
 * 在process.waitFor();前调用
 * @param process
 */
@Override
public void dealStream(Process process) {
    if (process == null) {
        return;
    }
    // 处理InputStream的线程
    new Thread() {
        @Override
        public void run() {
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = null;
            try {
                while ((line = in.readLine()) != null) {
                    //logger.info("output: " + line);
                    dealLine(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
    // 处理ErrorStream的线程
    new Thread() {
        @Override
        public void run() {
            BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line = null;
            try {
                while ((line = err.readLine()) != null) {
                    logger.info("err: " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    err.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}

}

正在上传…
取消
@Component
public class FileService {

public static Logger logger = LoggerFactory.getLogger(FileService.class);

// 下载小文件
public File downloadFile(String formUrl, String fileName) throws Throwable {
    File desc = null;
    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpGet httpget = new HttpGet(formUrl);
    httpget.setConfig(RequestConfig.custom() //
            .setConnectionRequestTimeout(5000) //
            .setConnectTimeout(5000) //
            .setSocketTimeout(5000) //
            .build());
    logger.debug("正在从{}下载文件到{}",formUrl,fileName);
    try (CloseableHttpResponse response = httpclient.execute(httpget)) {
        org.apache.http.HttpEntity entity = response.getEntity();
        desc = new File(fileName);
        try (InputStream is = entity.getContent(); //
                OutputStream os = new FileOutputStream(desc)) {
            StreamUtils.copy(is, os);
            logger.debug("成功从{}下载文件到{}",formUrl,fileName);
        }
    }
    return desc;
}

public void downloadLittleFileToPath(String url, String target) {
    Instant now = Instant.now();
    RestTemplate template = new RestTemplate();
    ClientHttpRequestFactory clientFactory = new HttpComponentsClientHttpRequestFactory();
    template.setRequestFactory(clientFactory);
    HttpHeaders header = new HttpHeaders();
    List<MediaType> list = new ArrayList<MediaType>();
    // 指定下载文件类型
    list.add(MediaType.APPLICATION_OCTET_STREAM);
    header.setAccept(list);
    HttpEntity<byte[]> request = new HttpEntity<byte[]>(header);
    ResponseEntity<byte[]> rsp = template.exchange(url, HttpMethod.GET, request, byte[].class);
    logger.info("[下载文件] [状态码] code:{}", rsp.getStatusCode());
    try {
        if(Paths.get(target).toFile().exists()) {
            Paths.get(target).toFile().delete();
        }
        Files.write(Paths.get(target), Objects.requireNonNull(rsp.getBody(), "未获取到下载文件"));
    } catch (IOException e) {
        logger.error("[下载文件] 写入失败:", e);
    }
    logger.info("[下载文件] 完成,耗时:{}", ChronoUnit.MILLIS.between(now, Instant.now()));
}

 public void downloadBigFileToPath(String url, String target) {
        Instant now = Instant.now();
        try {
            RestTemplate template = new RestTemplate();
            ClientHttpRequestFactory clientFactory = new HttpComponentsClientHttpRequestFactory();
            template.setRequestFactory(clientFactory);
          //定义请求头的接收类型
          RequestCallback requestCallback = request -> request.getHeaders()
                  .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
          // getForObject会将所有返回直接放到内存中,使用流来替代这个操作
          ResponseExtractor<Void> responseExtractor = response -> {
            // Here I write the response to a file but do what you like
            if(Files.exists(Paths.get(target), LinkOption.NOFOLLOW_LINKS)) {
                Files.delete(Paths.get(target));
            }
            Files.copy(response.getBody(), Paths.get(target));
            return null;
          };
          template.execute(url, HttpMethod.GET, requestCallback, responseExtractor);
        } catch (Throwable e) {
            logger.error("[下载文件] 写入失败:", e);
        }
        logger.info("[下载文件] 完成,耗时:{}", ChronoUnit.MILLIS.between(now, Instant.now()));
      }

}
FileService

有个问题需要注意:

转码目标文件必须不存在才行,如果存在 先删除,不然就卡死。

相关文章
|
3月前
|
编解码 NoSQL Java
使用Spring Boot + Redis 队列实现视频文件上传及FFmpeg转码的技术分享
【8月更文挑战第30天】在当前的互联网应用中,视频内容的处理与分发已成为不可或缺的一部分。对于视频平台而言,高效、稳定地处理用户上传的视频文件,并对其进行转码以适应不同设备的播放需求,是提升用户体验的关键。本文将围绕使用Spring Boot结合Redis队列技术来实现视频文件上传及FFmpeg转码的过程,分享一系列技术干货。
203 3
|
3月前
|
编解码 Linux
CentOS安装ffmpeg并转码视频为mp4
CentOS安装ffmpeg并转码视频为mp4
144 0
|
4月前
|
Java BI 数据处理
如何在Java中实现Excel操作
如何在Java中实现Excel操作
|
5月前
|
编解码
FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作
《FFmpeg开发实战》书中3.4.3节讲解如何将H.264流封装成MP4。H.264流通常以SPS→PPS→IDR帧开始,这一说法通过雷霄骅的H264分析器得到验证。分析器能解析H.264文件但不支持MP4。ZLMediaKit服务器在遇到I帧时会自动插入SPS和PPS配置帧,确保流符合标准格式。若缺少这些帧,客户端拉流时会报错。FFmpeg开发实战:从零基础到短视频上线》书中提供了更多FFmpeg开发细节。
134 0
FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作
|
1月前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
|
5月前
|
存储 Java
java用modbus4j的RTU去操作那些寄存器(线圈,保持,输入,离散输入寄存器)
java用modbus4j的RTU去操作那些寄存器(线圈,保持,输入,离散输入寄存器)
126 0
|
4月前
|
存储 Java 索引
Java ArrayList操作指南:如何移除并返回第一个元素
通过上述方法,你可以方便地从Java的 `ArrayList` 中移除并返回第一个元素。这种操作在日常编程中非常常见,是处理列表时的基本技能之一。希望这篇指南能帮助你更好地理解和运用Java的 `ArrayList`。
50 4
|
4月前
|
分布式计算 DataWorks Java
DataWorks操作报错合集之使用ODPS Tunnel Upload功能时,遇到报错:Java 堆内存不足,该如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
4月前
|
编解码
FFmpeg开发笔记(三十七)分析SRS对HLS协议里TS包的插帧操作
《FFmpeg开发实战》书中讲解了音视频封装格式,重点介绍了TS,因其固定长度和独立解码特性,常用于HLS协议。HLS通过m3u8文件指示客户端播放TS分片。SRS服务器在转换MP4至TS时,会在每个TS包头添加SPS和PPS帧,保证解码完整性。这一过程在SrsIngestHlsOutput::on_ts_video函数中体现,调用write_h264_sps_pps和write_h264_ipb_frame完成。详细实现涉及SrsRawH264Stream::mux_sequence_header函数,遵循ISO标准写入SPS和PPS NAL单元。
80 0
FFmpeg开发笔记(三十七)分析SRS对HLS协议里TS包的插帧操作
|
4月前
|
SQL 缓存 Java
使用MyBatis优化Java持久层操作
使用MyBatis优化Java持久层操作