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

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.mfbz.cn/a/774118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

你真的会ELISA加样吗?

在ELISA实验中&#xff0c;研究人员需要进行多次加样步骤完成实验操作。对于常规双抗体夹心法ELISA&#xff0c;一般有如下加样步聚&#xff0c;即加样本、加检测抗体、加酶结合物、加底物&#xff08;最后加终止液停止反应&#xff09;。 加样步骤基础知识 加样步骤中一般使用…

华为OD机试2024年最新题库 JAVA C卷+D卷

目录 专栏导读华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如何刷题更有效率呢&#xff1f; 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、链表5、栈6、滑动窗口7、二叉树8、并查集9、矩阵 三、算法1、基础算法① 贪心思维② 二分查…

解决npm与yarn痛点:幽灵依赖与依赖分身

前言 在现代前端开发流程中&#xff0c;包管理工具扮演着至关重要的角色&#xff0c;其中npm和yarn是两个非常流行的JavaScript包管理工具。虽然它们为开发者提供了极大的便利&#xff0c;但也存在一些痛点&#xff0c;特别是关于“幽灵依赖&#xff08;Phantom Dependencies&a…

开放式耳机哪个牌子好?五款畅销产品推荐,免交智商税!

作为开放式耳机的测评博主&#xff0c;在最近又淘到了几款比较不错的开放式耳机&#xff0c;所以今天这篇文章&#xff0c;我也给大家推荐五款开放式耳机&#xff0c;内附还有我自己总结的开放式耳机的指南&#xff0c;希望各位小伙伴也能够看的开心&#xff0c;挑选到自己比较…

taoCMS v3.0.2 文件上传漏洞(CVE-2022-23880)

前言 CVE-2022-23880是一个影响taoCMS v3.0.2的任意文件上传漏洞。攻击者可以利用此漏洞通过上传特制的PHP文件在受影响的系统上执行任意代码。 漏洞细节 描述: 在taoCMS v3.0.2的文件管理模块中存在任意文件上传漏洞。攻击者可以通过上传恶意的PHP文件来执行任意代码。 影响…

YUM——简介、安装(Ubuntu22.04)

1、简介 YUM&#xff08;Yellowdog Updater, Modified&#xff09;是一个开源的命令行软件包管理工具&#xff0c;主要用于基于 RPM 包管理系统的 Linux 发行版&#xff0c;如 CentOS、Red Hat Enterprise Linux (RHEL) 和 Fedora。YUM 使用户能够轻松地安装、更新、删除和管理…

识别 Spring Cloud 配置文件的规则:Nacos, Bootstrap, Application

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

vue3【提效】使用 VueUse 高效开发(工具库 @vueuse/core + 新增的组件库 @vueuse/components)

Vueuse 是一个功能强大的 Vue.js 生态系统工具库&#xff0c;提供了可重用的组件和函数&#xff0c;帮助开发者更轻松地构建复杂的应用程序。 官网 &#xff1a;https://vueuse.org/core/useWindowScroll/ 安装 VueUse npm i vueuse/core vueuse/components&#xff08;可选&a…

收银系统源码-ERP报损管理

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

揭秘!为何电路工程师都爱用在线化电路仿真软件?7大理由让你秒懂!

在科技日新月异的今天&#xff0c;电路设计与仿真技术已经成为电子工程领域不可或缺的一部分。随着在线化电路仿真软件的兴起&#xff0c;越来越多的工程师选择使用这种便捷、高效的工具来辅助他们的工作。那么&#xff0c;究竟是什么让在线化电路仿真软件如此受欢迎呢&#xf…

强烈建议!所有Python基础差的同学,死磕这本64页的背记手册!

Python背记手册是一份非常实用的学习资料&#xff0c;它涵盖了Python语言的基础知识、语法规则、常用函数和模块等内容&#xff0c;对于初学者和有一定基础的Python程序员来说都非常有用。通过背诵这份手册&#xff0c;可以加深对Python语言的理解和记忆&#xff0c;提高编程能…

Datadog Dash 2024 新功能解析

Datadog 2024 年的 Dash 刚刚落下帷幕&#xff0c;作为正在与 Datadog 开始竞争的观测云&#xff0c;我们认真仔细的分析了 Datadog 的每一个新功能&#xff0c;发现一些很有意思的事情&#xff0c;今天就给大家做一次全面的分析。&#xff08;所有 Datadog 的 Dash 的最新功能…

【解码现代 C++】:实现自己的智能 【String 类】

目录 1. 经典的String类问题 1.1 构造函数 小李的理解 1.2 析构函数 小李的理解 1.3 测试函数 小李的理解 1.4 需要记住的知识点 2. 浅拷贝 2.1 什么是浅拷贝 小李的理解 2.2 需要记住的知识点 3. 深拷贝 3.1 传统版写法的String类 3.1.1 拷贝构造函数 小李的理…

Pspice添加新的元器件

1.下载好的Pspice的模型文件。 2.将模型文件的&#xff0c;识别类型修改为 lib 选择Pspice的模型路径 会立马跳出&#xff0c;下面的这个窗口。 核实元器件图形&#xff0c;没问题。 添加Pspic仿真模型文件 验证&#xff0c;是否添加模型文件成功 使用模型文件

学会python——用python制作一个登录和注册窗口(python实例十八)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.登录和注册窗口 3.1 代码构思 3.2 代码实例 3.3 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读…

扫地机器人如何利用图算法来进行避障策略和优化清扫路径的?

前言 扫地机器人是现代家庭中最常见的智能设备。其基本的核心组件由主控系统&#xff08;大脑&#xff09;、传感器等控制系统&#xff08;感知系统&#xff09;、动力供应系统&#xff08;心脏&#xff09;、清扫系统&#xff08;四肢&#xff09;组成。 扫地机器人的智能、高…

【中项第三版】系统集成项目管理工程师 | 第 9 章 项目管理概论② | 9.4 - 9.5

前言 第 9 章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节理论性较强&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 9.4 项目生命周期和项目阶段 9.4.1 定义与特征 9.4.2 生命周期类型 9.5 项目立项管理 9.5.1 项目建议与立项申请 9.5.2 …

交换数字00

题目链接 交换数字 题目描述 注意点 numbers.length 2-2147483647 < numbers[i] < 2147483647 解答思路 不适用临时变量&#xff0c;可以先将numbers[0]和numbers[1]的信息都存到某个位置&#xff08;可以相加可以相减或其他位操作&#xff09;&#xff0c;然后另一…

昇思MindSpore学习笔记4-02生成式--DCGAN生成漫画头像

摘要&#xff1a; 记录了昇思MindSpore AI框架使用70171张动漫头像图片训练一个DCGAN神经网络生成式对抗网络&#xff0c;并用来生成漫画头像的过程、步骤。包括环境准备、下载数据集、加载数据和预处理、构造网络、模型训练等。 一、概念 深度卷积对抗生成网络DCGAN Deep C…

MMSC物料库位扩充

MMSC物料库位扩充 输入事务码MMSC&#xff1a; 回车后添加新的库位即可&#xff1a; 代码实现&#xff0c;使用BDC *&------------------------------------------------* *&BDC的定义 *&------------------------------------------------* DATA gt_bdcdata T…