h5 video 标签播放经过 java 使用 ws.schild( jave、ffmpeg ) 压缩后的 mp4 视频只有声音无画面的问题排查记录

news2024/12/23 1:54:41

1. 引入  ws.schild MAVEN 依赖:


        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-all-deps</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-nativebin-win64</artifactId>
            <version>3.5.0</version>
        </dependency>

2. MyVideoUtils:



import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.AudioInfo;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.info.VideoInfo;
import ws.schild.jave.info.VideoSize;
import ws.schild.jave.progress.EncoderProgressListener;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.Set;

 
public class MyVideoUtils {
 
public static void main(String[] args) throws EncoderException {
        // 压缩前文件路径
        File source = new File("D:\\素材\\视频\\video_0001.mp4");

        // 压缩后的文件路径
        File target = new File("D:\\素材\\视频\\video_0001_output1.mp4");


        compress( source,target,0.3 );
}
 
   
    public static void compress(File sourceVideoFile, File targetVideoFile, Double scale) throws EncoderException {
        try {
            System.out.println("---------------开始压缩---------------");
            long start = System.currentTimeMillis();

            // 尺寸的比例
            BigDecimal scale_size = BigDecimal.valueOf(scale);

            // 码率相关的比率
            BigDecimal scale_rate = BigDecimal.valueOf(scale)
                                                .multiply(BigDecimal.valueOf(scale));

            // 输出源视频的信息
            MultimediaObject multimediaObject_source = new MultimediaObject(sourceVideoFile);
            MultimediaInfo multimediaInfo_source = multimediaObject_source.getInfo();
            AudioInfo audioInfo_source = multimediaInfo_source.getAudio();
            VideoInfo videoInfo_source = multimediaInfo_source.getVideo();

            // 时长
            long seconds = multimediaInfo_source.getDuration() / 1000L;
            // 每秒几帧
            float frameRate = videoInfo_source.getFrameRate();

            System.out.println( "seconds = " + seconds );
            System.out.println( "frameRate = " + frameRate );
            int totalFrame = BigDecimal.valueOf(seconds).multiply(BigDecimal.valueOf(frameRate)).intValue();
            System.out.println( "totalFrame = " + totalFrame );

            System.out.println( "原视频 bitRate = " + videoInfo_source.getBitRate() );
            System.out.println( "原视频 frameRate = " + videoInfo_source.getFrameRate() );
            System.out.println( "原视频 decoder = " + videoInfo_source.getDecoder() );
            VideoSize videoSize_source = videoInfo_source.getSize();
            System.out.println( "源视频宽x高:" + videoSize_source.getWidth() + "x" + videoSize_source.getHeight() );


            Map<String, String> metadata = videoInfo_source.getMetadata();
            Set<String> keys = metadata.keySet();
            for( String key:keys ){
                System.out.println( key + " = " + metadata.get( key ) );
            }
            System.out.println();

            // 音频编码属性配置
            AudioAttributes audioAttributes = new AudioAttributes();
            audioAttributes.setCodec("libmp3lame");

            int audioBitRate_new = BigDecimal.valueOf(audioInfo_source.getBitRate()).multiply(scale_rate).intValue();
            System.out.println( "audioBitRate_new = " + audioBitRate_new );
            audioAttributes.setBitRate( audioBitRate_new  );
            // 设置重新编码的音频流中使用的声道数(1 =单声道,2 = 双声道(立体声))
            audioAttributes.setChannels(1);

            // int audioSamplingRate_new = BigDecimal.valueOf(audioInfo_source.getSamplingRate()).multiply(scale_rate).intValue();
            // System.out.println( "audioSamplingRate_new = " + audioSamplingRate_new );
            // todo 设置此值报错 "ws.schild.jave.EncoderException: Exit code of ffmpeg encoding run is 1",暂不知道什么原因???
            // audioAttributes.setSamplingRate( audioSamplingRate_new );

            // 视频编码属性配置
            VideoAttributes videoAttributes = new VideoAttributes();
            // 设置编码
            // videoAttributes.setCodec("mpeg4");
            videoAttributes.setCodec( "h264" );

            int videoBitRate_new = BigDecimal.valueOf(videoInfo_source.getBitRate()).multiply(scale_rate).intValue();
            System.out.println( "videoBitRate_new = " + videoBitRate_new );
            videoAttributes.setBitRate( videoBitRate_new );

            int newHeight = BigDecimal.valueOf(videoInfo_source.getSize().getHeight()).multiply(scale_size).intValue();
            int newWidth = BigDecimal.valueOf(videoInfo_source.getSize().getWidth()).multiply(scale_size).intValue();
            //  新的宽高都必须是2的整数倍!!!
            newHeight = MyMathUtils.getClosestNuumberThatCanBeDividedBy2( newHeight );
            newWidth = MyMathUtils.getClosestNuumberThatCanBeDividedBy2( newWidth );
            VideoSize videoSize_new = new VideoSize( newWidth,newHeight );
            videoAttributes.setSize( videoSize_new );

            // 编码设置
            EncodingAttributes encodingAttributes = new EncodingAttributes();
            encodingAttributes.setOutputFormat("mp4");
            encodingAttributes.setAudioAttributes( audioAttributes );
            encodingAttributes.setVideoAttributes( videoAttributes );
            // 设置值编码
            Encoder encoder = new Encoder();
            // encoder.encode( multimediaObject_source, targetVideoFile, encodingAttributes );
            // 压缩转换进度监听
            EncoderProgressListener encoderProgressListener = new EncoderProgressListener() {
                @Override
                public void sourceInfo(MultimediaInfo info) {
                }

                @Override
                public void progress(int permil) {
                    double processPercent = BigDecimal.valueOf(permil)
                                                        .divide(BigDecimal.valueOf(1000d), 4, RoundingMode.HALF_UP)
                                                        .doubleValue();
                    System.out.println( "压缩转换进度:" + MyMathUtils.double2PercentFormat( processPercent ) );
                }

                @Override
                public void message(String message) {
                    System.out.println( "message ------------------------------> " + message );
                }
            };
            encoder.encode( multimediaObject_source, targetVideoFile, encodingAttributes,encoderProgressListener );

            System.out.println("---------------结束压缩---------------");
            long end = System.currentTimeMillis();
            System.out.println("压缩前大小:"+ MyMathUtils.byte2M( sourceVideoFile.length() ) + "M,压缩后大小:" + MyMathUtils.byte2M( targetVideoFile.length() ) + "M" );
            System.out.println("压缩耗时:" + MyMathUtils.mill2Second(  ( end - start ) ) + "秒" );
        } catch (EncoderException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
}

3. MyMathUtils:

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

public class MyMathUtils {

    public static Integer getScale(Double num) {
        if( num == null || num == 0d ){
            return 0;
        }
        double decimalPart = BigDecimal.valueOf(num).subtract(BigDecimal.valueOf(num.intValue())).doubleValue();
        if( decimalPart == 0d ){
            return 0;
        }
        String decimalPartStr = String.valueOf(decimalPart);
        String decimalPartStr1 = decimalPartStr.substring( decimalPartStr.indexOf(".") + 1 );
        return decimalPartStr1.length();
    }

    /**
     * 计算 Double 集合中不为空并且大于0的元素的个数
     * @param nums
     * @return
     */
    public static Integer calculateCountForNotNullAndBiggerThanZero(List<Double> nums) {
        if( nums == null || nums.size()== 0 ){
            return 0;
        }
        Integer count = 0;
        for( Double num:nums ){
            if( num != null && num > 0d ){
                count++;
            }
        }
        return count;
    }

    public static Double calculateSum(List<Double> nums) {
        if( nums == null || nums.size() == 0 ){
            return 0d;
        }
        BigDecimal sum = BigDecimal.ZERO;
        for( Double num:nums ){
            if( num == null || num == 0d ){
                continue;
            }
            sum = sum.add( BigDecimal.valueOf( num ) );
        }
        return sum.doubleValue();
    }

    public static Double nullDouble2Zero(Double num) {
        if( num == null ){
            return 0d;
        }
        return num;
    }

    public static Integer nullInteger2Zero(Integer num) {
        if( num == null ){
            return 0;
        }
        return num;
    }

    public static Double float2Double(Float f) {
        if( f == null ){
            return null;
        }
        return f.doubleValue();
    }

    public static String double2PercentFormat(Double d) {
        if( d == null ){
            d = 0d;
        }
        double d_percent = BigDecimal.valueOf(d).multiply(BigDecimal.valueOf(100d)).setScale(4, RoundingMode.HALF_UP).doubleValue();
        return d_percent + "%";
    }

    public static Double byte2M( Long byteLenth ){
        return BigDecimal.valueOf( byteLenth )
                            .divide( BigDecimal.valueOf( 1024d ),4,RoundingMode.HALF_UP )
                            .divide( BigDecimal.valueOf( 1024d ),4,RoundingMode.HALF_UP )
                            .doubleValue();
    }

    public static Double mill2Second( Long mill ){
        return BigDecimal.valueOf( mill )
                .divide( BigDecimal.valueOf( 1000d ),4,RoundingMode.HALF_UP )
                .doubleValue();
    }

    /**
     * 获得与传入的数字 num 最接近的能被2整除的数字
     * @param num
     * @return
     */
    public static int getClosestNuumberThatCanBeDividedBy2(int num) {
        int result = BigDecimal.valueOf(BigDecimal.valueOf(num)
                                                    .divide(BigDecimal.valueOf(2d), 2, RoundingMode.HALF_UP)
                                                    .intValue())
                                .multiply(BigDecimal.valueOf(2d))
                                .intValue();
        return result;
    }
}

4. 将压缩后的文件上传至文件服务器( 例如 minio )获得在线 url,例如 :https://xxx.xxx.com/minio/xxx-bucket/20240705131522-124765sd65sad65sa6d7asd6sa7d56235e675sadasd.mp4,并使用 ht  video 标签播放:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MP4 Video Player Demo</title>
</head>
<body>

    <video width="320" height="240" controls>
        <source src="https://xxx.xxx.com/minio/xxx-bucket/20240705131522-124765sd65sad65sa6d7asd6sa7d56235e675sadasd.mp4" type="video/mp4">
        您的浏览器不支持Video标签。
    </video>
</body>
</html>

播放效果如下:

只有声音没有画面,但是播放压缩之前的原始视频就ok,使用 vlc查看下视频编码信息:

发现编解码器不一样,于是尝试将编解码器换成H264试一下:

 // videoAttributes.setCodec("mpeg4");
 videoAttributes.setCodec( "h264" );

重新压缩后播放出画面了:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1898975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Spring Boot框架的EAM系统设计与实现

摘 要&#xff1a;文章设计并实现一个基于Spring Boot框架的EAM系统&#xff0c;以应对传统人工管理模式存在的低效与信息管理难题。系统利用Java语言、JSP技术、MySQL数据库等技术栈&#xff0c;构建了一个B/S架构的高效管理平台&#xff0c;提升了资产管理的信息化水平。该系…

分班查询系统怎么制作?

新学年的临近&#xff0c;教师们的工作清单再次膨胀&#xff0c;充满各种任务。开学前的准备总是让人忙碌不已&#xff0c;从课程规划到教室布置&#xff0c;再到与家长的沟通&#xff0c;每一个环节都至关重要。尤其是分班结果的公布&#xff0c;这项工作虽然看起来简单&#…

Qwen1.5-1.8b部署

仿照ChatGLM3部署&#xff0c;参考了Qwen模型的文档&#xff0c;模型地址https://modelscope.cn/models/qwen/Qwen1.5-1.8B-Chat/summary http接口 服务端代码api.py from fastapi import FastAPI, Request from transformers import AutoTokenizer, AutoModelForCausalLM, …

强化学习后的数学原理:随机近似与梯度下降

概述 这节课的作用&#xff1a; 本节课大纲如下&#xff1a; Motivating examples 先回顾一下 mean estimation &#xff1a; 为什么总数反复提到这个 mean estimation&#xff0c;就是因为 RL 当中有非常多的 expectation&#xff0c;后面就会知道除了 state value 这些定义…

PySide6 实现资源的加载:深入解析与实战案例

目录 1. 引言 2. 加载内置资源 3. 使用自定义资源文件&#xff08;.qrc&#xff09; 创建.qrc文件 编译.qrc文件 加载资源 4. 动态加载UI文件 使用Qt Designer设计UI 加载UI文件 5. 注意事项与最佳实践 6. 结论 在开发基于PySide6的桌面应用程序时&…

博途通讯笔记1:1200与1200之间S7通讯

目录 一、添加子网连接二、创建PUT GET三、各个参数的意义 一、添加子网连接 二、创建PUT GET 三、各个参数的意义

新手高效指南:电子元器件BOM表创建/制作及配单全教程

在科技日新月异的今天&#xff0c;电子产品设计与制造不仅是创新精神的展现&#xff0c;更是对精确度与效率的不懈追求。在这个过程中&#xff0c;一份精细且全面的BOM&#xff08;物料清单&#xff09;犹如一座桥梁&#xff0c;连接着创意与现实世界。BOM不仅细致记录了产品所…

如何优化圆柱晶振32.768KHz的外壳接地?

圆柱晶振32.768KHz在电子设备中扮演着重要的角色&#xff0c;其精确的时钟信号对于许多应用至关重要。为了确保晶振的稳定性和准确性&#xff0c;外壳接地是一个关键步骤。 一、外壳接地的目的 外壳接地的主要目的是为了防止信号干扰。当晶振的外壳接地后&#xff0c;它相当于…

16-JS封装:extend方法

目录 一、封装需求 二、实现1&#xff1a;jQuery.extend 三、实现2&#xff1a;通过原型jQuery.fn.extend 四、优化 一、封装需求 封装需求&#xff1a; $.extend&#xff1a; var obj{ name:"xxx",age:18} var obj3{ gender:"女"} var obj2{}; 将obj、…

S7-1200PLC学习记录

文章目录 前言一、S7-12001.数字量输入模块2. PNP接法和NPN接法 二、博图软件1. 位逻辑运算Part1. 添加新设备&#xff08;添加PLC&#xff09;Part2. 添加信号模块Part3. 添加信号板中模块Part4. 添加新块Part5. Main编程文件案例1案例2 -( S )- 和 -( R )-完整操作过程&#…

ERROR | Web server failed to start. Port 8080 was already in use.

错误提示&#xff1a; *************************** APPLICATION FAILED TO START ***************************Description:Web server failed to start. Port 8080 was already in use.Action:Identify and stop the process thats listening on port 8080 or configure thi…

C++——模板详解(下篇)

一、非类型模板参数 模板参数分为类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之后的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类&#xff08;函数&#xff09;模板的一个参数&#xff0c;在类&#…

Swift 中的方法调用机制

Swift 方法调用详解&#xff1a;与 Objective-C 的对比、V-Table 机制、Witness Table 机制 在 iOS 开发中&#xff0c;Swift 和 Objective-C 是两种常用的编程语言。尽管它们都能用于开发应用程序&#xff0c;但在方法调用的底层机制上存在显著差异。本文将详细介绍 Swift 的…

maven项目使用netty,前端是vue2,实现通讯

引入的java包 <!-- 以下是即时通讯--><!-- Netty core modules --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.76.Final</version> <!-- 使用最新的稳定版本…

C++中的引用——引用的注意事项

1.引用必须初始化 2.引用在初始化后不可以改变 示例&#xff1a; 运行结果&#xff1a;

03:EDA的进阶使用

使用EDA设计一个38译码器电路和245放大电路 1、38译码器1.1、查看74HC138芯片数据1.2、电路设计 2、245放大电路2.1、查看数据手册2.2、设计电路 3、绘制PCB3.1、导入3.2、放置3.3、飞线3.4、特殊方式连接GND3.5、泪滴3.6、配置丝印和划分区域3.7、添加typc接口供电 1、38译码器…

案例精选 | 聚铭网络助力南京市玄武区教育局构建内网日志审计合规体系

南京市玄武区教育局作为江苏省教育领域的先锋机构&#xff0c;其工作重点涵盖了教育政策的实施、教育现代化与信息化的融合、教育资源的优化、教育质量的提升以及教育公平的促进。在这一背景下&#xff0c;网络安全管理成为了确保教育信息化顺利推进的关键环节之一。 根据玄武…

【CV炼丹师勇闯力扣训练营 Day24:§7 回溯3】

CV炼丹师勇闯力扣训练营 代码随想录算法训练营第24天 93 复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 ‘.’ 分隔。 例如&#xff1a;“0.1.2.201” 和 “192.168.…

tensorboard安装失败,怎么办?

标题tensorboard未安装报错&#xff0c;如下图所示&#xff1a; 解决方法尝试&#xff1a; 这些方法都可以试一下&#xff0c;因为每个人的情况可能不同&#xff0c;成功的方法也不同。 1、在pycharm中Pip失败 2、 在pycharm中Conda 失败 3、在Pycharm中换清华源失败 4、直接…

软件是什么?一个软件到底是哪些部分组成的-软件到底有哪些分支呢?

https://doc.youyacao.com/117/2163 软件是什么&#xff1f;一个软件到底是哪些部分组成的-软件到底有哪些分支呢&#xff1f; 何为软件 软件定义 的本质是通过软件编程实现硬件资源的虚拟化、灵活、多样和定制化功能&#xff0c;以最大化系统运行效率和能量效率。它基于硬…