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() {@Overridepublic void sourceInfo(MultimediaInfo info) {}@Overridepublic void progress(int permil) {double processPercent = BigDecimal.valueOf(permil).divide(BigDecimal.valueOf(1000d), 4, RoundingMode.HALF_UP).doubleValue();System.out.println( "压缩转换进度:" + MyMathUtils.double2PercentFormat( processPercent ) );}@Overridepublic 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.mzph.cn/pingmian/41406.shtml

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

相关文章

idm下载慢怎么回事 idm批量导入下载使用方法

IDM (Internet Download Manager)是一款兼容性大&#xff0c;支持多种语言的下载管理软件&#xff0c;它可以自动检测并下载网页上的内容&#xff0c;这正是这一优点&#xff0c;使得它受到了广大用户的喜爱。在日常使用互联网的过程中&#xff0c;快速下载文件对我们来说非常重…

Victor CMS v1.0 SQL 注入漏洞(CVE-2022-26201)

前言 CVE-2022-26201 是 Victor CMS v1.0 中发现的一个 SQL 注入漏洞。该漏洞允许攻击者通过特制的 SQL 查询注入到应用程序中&#xff0c;从而访问或操作数据库中的数据。以下是详细信息&#xff1a; 漏洞描述&#xff1a; 类型&#xff1a;SQL 注入 (SQL Injection)影响版本…

LLM - 神经网络的训练过程

1. 对于回归问题&#xff0c;用损失函数来计算预测值和真实值的差异&#xff0c;一种常用的公式是如下图所示(Mean Square Error)&#xff0c;如果损失函数的值越小说明神经网络学习越准确&#xff0c;所以神经网络训练目标是减小损失函数的值&#xff0c; 2. 对于分类问题&…

并发、多线程和HTTP连接之间有什么关系?

一、并发的概念 并发是系统同时处理多个任务或事件的能力。在计算中&#xff0c;这意味着系统能够在同一时间段内处理多个任务&#xff0c;而不是严格按照顺序一个接一个地执行它们。并发提高了系统的效率和资源利用率&#xff0c;从而更好地满足用户的需求。在现代应用程序中&…

SwiftUI中List的liststyle样式及使用详解添加、移动、删除、自定义滑动

SwiftUI中的List可是个好东西&#xff0c;它用于显示可滚动列表的视图容器&#xff0c;类似于UITableView。在List中可以显示静态或动态的数据&#xff0c;并支持垂直滚动。List是一个数据驱动的视图&#xff0c;当数据发生变化时&#xff0c;列表会自动更新。针对List&#xf…

神领物流项目第一天

文章目录 聚焦快递领域首先第一个是验证码模块流程登录接口权限管家 聚焦快递领域 首先第一个是验证码模块流程 首先生成验证码的流程 可以使用工具类去生成验证码 LineCaptcha lineCaptcha CaptchaUtil.createLineCaptcha(160, 60, 4, 26);// 获取值然后存入redis中 strin…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(二十二)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 32 节&#xff09; P32《31.通知-基础通知》 基础文本类型通知&#xff1a;briefText 没有用&#xff0c;写了也白写。 长文本类型…

【东奥会计-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

elasticsearch-users和elasticsearch-reset-password介绍

elasticsearch 内置 elastic, kibana, logstash_system,beats_system 共4个用户&#xff0c;用途如下&#xff1a; elastic 账号&#xff1a;内置的超级用户&#xff0c;拥有 superuser 角色。 kibana 账号&#xff1a;用来连接 elasticsearch 并与之通信。Kibana 服务器以该用…

【HICE】基于用户认证的虚拟服务搭建

1.创建特定的内容 --账号与密码&#xff08;需要认证访问&#xff09;【里面】 2.编辑配置1.conf的内容&#xff0c;更新httpd 3.编辑hehe网页&#xff08;外部公开&#xff09; cd /www/ echo hehe > hehe/index.html 4.更改本地hosts和window下的解析 5.浏览器下验证内…

OpenCV基础(1)

目录 安装OpenCV 读取图像 显示图像 cv2.waitKey()函数 cv2.destroyAllWindows()函数 保存图像 读取视频 开启摄像头 图像处理 像素处理 二值图像及灰度图像 彩色图像及通道处理 调整图像大小 感兴趣区域 掩模 掩模基础及构造 掩模作为函数参数 色彩处理 色…

CSRF漏洞攻击

05-CSRF 1 CSRF概述 1.1 概述 CSRF (Cross-Site Request Forgery) 跨站请求伪造&#xff0c;也可称为一键式攻击 (one-click-attack)&#xff0c;通常缩写为 CSRF 或者 XSRF。 CSRF 攻击是一种挟持用户在当前已登录的浏览器上发送恶意请求的攻击方法。相对于XSS利用用户对指…

wsl安装Linux系统到指定位置

默认情况下,wsl安装的系统,会安装到系统C盘,长期下去,很容易把C盘的空间消耗完,从而影响系统的正常运行,所以我建议是将wsl所有的系统都安装到其它磁盘中,便于维护。 1、导出镜像 通过wsl -l -v 查看当前已安装的系统版本。 导出到当前目录位置,也可以指定目录位置。 w…

docker集群部署主从mysql

搭建一个mysql集群&#xff0c;1主2从&#xff0c;使用docker容器 一、创建docker的mysql镜像 下次补上&#xff0c;因为现在很多网络不能直接pull&#xff0c;操作下次补上。 二、创建mysql容器 创建容器1 docker run -it -d --name mysql_1 -p 7001:3306 --net mynet --…

《昇思25天学习打卡营第10天|使用静态图加速》

文章目录 今日所学&#xff1a;一、背景介绍1. 动态图模式2. 静态图模式 三、静态图模式的使用场景四、静态图模式开启方式1. 基于装饰器的开启方式2. 基于context的开启方式 总结&#xff1a; 今日所学&#xff1a; 在上一集中&#xff0c;我学习了保存与加载的方法&#xff…

昇思25天学习打卡营第6天|简单的深度学习模型实战 - 函数式自动微分

自动微分(Automatic Differentiation)是什么&#xff1f;微分是函数在某一处的导数值&#xff0c;自动微分就是使用计算机程序自动求解函数在某一处的导数值。自动微分可用于计算神经网络反向传播的梯度大小&#xff0c;是机器学习训练中不可或缺的一步。 这些公式难免让人头大…

论文浅尝 | 从最少到最多的提示可在大型语言模型中实现复杂的推理

笔记整理&#xff1a;王泽元&#xff0c;浙江大学博士 链接&#xff1a;https://openreview.net/forum?idWZH7099tgfM 1. 动机 尽管深度学习已经取得了巨大的成功&#xff0c;但它与人类智慧仍然存在一些明显差距。这些差距包括以下几个方面&#xff1a;1&#xff09;学习新任…

【代码大全2 选读】看看骨灰级高手消灭 if-else 逻辑的瑞士军刀长啥样

文章目录 1 【写在前面】2 【心法】这把瑞士军刀长啥样3 【示例1】确定某个月份的天数&#xff08;Days-in-Month Example&#xff09;4 【示例2】确定保险费率&#xff08;Insurance Rates Example&#xff09;5 【示例3】灵活的消息格式&#xff08;Flexible-Message-Format …

14-27 剑和诗人 1 – 请称呼我AI工程师

​​​​​ 仅初创企业的收入就超过 10 亿美元&#xff0c;随着 Gen AI 的早期成功迹象&#xff0c;每家有远见的科技公司都在竞相将 Gen AI 功能融入其产品、客户支持机器人和营销中。作为一种技术&#xff0c;AI 正处于与 90 年代末互联网相似的阶段&#xff0c;甚至完全相同…

【unity实战】Unity中使用A*寻路+有限状态机制作一个俯视角敌人AI

最终效果 文章目录 最终效果前言A*寻路插件介绍下载导入AI插件生成寻路网格节点的类型障碍物寻路测试A*只打印报错信息 代码控制寻路动画配置敌人状态机各种状态脚本效果完结 前言 前面做过有限状态机制作一个敌人AI&#xff1a;【unity实战】在Unity中使用有限状态机制作一个…