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://xiahunao.cn/news/3224066.html

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

相关文章

SQL 与 NoSQL 数据库:一场关于灵活性与结构的对话

文章目录 引言SQL 数据库&#xff1a;传统之光定义特征优势缺点 NoSQL 数据库&#xff1a;新时代的弹性定义特征优势缺点 何时选择 NoSQL&#xff1f;场景1&#xff1a;海量数据与高并发场景2&#xff1a;灵活性需求场景3&#xff1a;实时数据分析场景4&#xff1a;分布式系统 …

2024年7月1日,公布的OpenSSH的漏洞【CVE-2024-6387】

目录 ■概要 ■概要&#xff08;日语&#xff09; ■相关知识 openssh 和 ssh 有区别吗 如何查看 openssh的版本 漏洞描述 glibc Linux是什么 如何查看系统是不是基于 Gibc RHEL Linux 是基于Glibc的Linux吗 还有哪些 Linux版本是基于 GNU C库&#xff08;glibc&…

算力狂飙|WAIC 2024上的服务器

7月7日&#xff0c;2024世界人工智能大会暨人工智能全球治理高级别会议&#xff08;WAIC 2024&#xff09;在上海落下帷幕。这场备受瞩目的AI盛宴与热辣夏日碰撞&#xff0c;吸引了全球科技、产业及学术界的广泛关注&#xff0c;线下参观人数突破30万人次&#xff0c;线上流量突…

gitlab-runner安装部署CI/CD

手动安装 卸载旧版&#xff1a; gitlab-runner --version gitlab-runner stop yum remove gitlab-runner下载gitlab对应版本的runner # https://docs.gitlab.com/runner/install/bleeding-edge.html#download-any-other-tagged-releasecurl -L --output /usr/bin/gitlab-run…

通用后台管理(二)——项目搭建

目录 前言 一、安装vue-cli依赖 1、使用yarn下载vue-cli 2、使用npm下载 3、检查一下是否下载成功 二、创建项目 1、创建项目&#xff0c;my-app是项目名称 2、 这里选择vue 2&#xff0c;蓝色表示选中的。 3、启动项目 三、下载项目依赖 四、配置项目 1、修改esli…

【从零开始实现stm32无刷电机FOC】【理论】【3/6 位置、速度、电流控制】

目录 PID控制滤波单独位置控制单独速度控制单独电流控制位置-速度-电流串级控制 上一节&#xff0c;通过对SVPWM的推导&#xff0c;我们获得了控制电机转子任意受力的能力。本节&#xff0c;我们选用上节得到的转子dq轴解耦的SVPWM形式&#xff0c;对转子受力进行合理控制&…

STM32实战篇:按键控制LED

按键控制LED 功能要求 有两个按键&#xff0c;分别控制两个LED灯。当按键按下后&#xff0c;灯的亮暗状态改变。实物如下图所示&#xff1a; 由图可知&#xff0c;按键一端直接接地&#xff0c;故另一端所对应IO引脚的输入模式应该为上拉输入模式。 实现代码 #include "…

nvm下载

nvm下载 1.下载nvm安装包2.安装nvm3.修改settings.txt4.安装成功5.继续配置 下载nvm之前,你最好将你电脑上的node卸载掉,直接在winx中卸载就行 1.下载nvm安装包 https://github.com/coreybutler/nvm-windows/releases 2.安装nvm 3.修改settings.txt root: E:\nvm\install\nv…

DMA方式的知识点笔记

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 目录 1. DMA基本概念 2. DMA传送过程 易错点 DMA控制器操作流程 3. DMA传送方式 这是单总线的结果 &#xff08;CPU说了算 所以不会产生于CPU的冲突&#xff09; 这…

新闻资讯整合平台:一站式满足企业信息需求

摘要&#xff1a; 面对信息爆炸的时代&#xff0c;企业如何在海量数据中快速获取有价值资讯&#xff0c;成为提升竞争力的关键。本文将探讨如何通过一站式新闻资讯整合平台&#xff0c;实现企业信息需求的全面满足&#xff0c;提升决策效率&#xff0c;同时介绍实用工具推荐&a…

Transformer中的编码器和解码器结构有什么不同?

Transformer背后的核心概念&#xff1a;注意力机制&#xff1b;编码器-解码器结构&#xff1b;多头注意力等&#xff1b; 例如&#xff1a;The cat sat on the mat&#xff1b; 1、嵌入&#xff1a; 首先&#xff0c;模型将输入序列中的每个单词嵌入到一个高维向量中表示&…

Vuforia AR篇(八)— AR塔防上篇

目录 前言一、设置Vuforia AR环境1. 添加AR Camera2. 设置目标图像 二、创建塔防游戏基础1. 导入素材2. 搭建场景3. 创建敌人4. 创建脚本 前言 在增强现实&#xff08;AR&#xff09;技术快速发展的今天&#xff0c;Vuforia作为一个强大的AR开发平台&#xff0c;为开发者提供了…

工业机床CNC设备如何上云?

工业机床CNC设备如何上云&#xff1f; 工业机床的计算机数控&#xff08;CNC&#xff09;设备实现远程监控数据上云&#xff0c;是现代制造业智能化转型的关键一环。这一过程不仅能够实时监测设备状态、优化生产流程&#xff0c;还能通过大数据分析提升生产效率与产品质量&…

数据结构--二叉树相关习题5(判断二叉树是否是完全二叉树 )

1.判断二叉树是否是完全二叉树 辨别&#xff1a; 不能使用递归或者算节点个数和高度来判断。 满二叉树可以用高度和节点来判断&#xff0c;因为是完整的。 但是完全二叉树前面是满的&#xff0c;但是最后一层是从左到右连续这种 如果仍然用这种方法的话&#xff0c;如下图…

Chromium编译指南2024 Linux篇-同步Chromium第三方库(四)

1.引言 在成功拉取Chromium源码并创建新分支后&#xff0c;我们需要进一步配置开发环境。这包括拉取必要的第三方库以及设置hooks&#xff0c;以确保我们能够顺利进行编译和开发工作。以下步骤将详细介绍如何进行这些配置。 2.拉取第三方库以及hooks Chromium 使用了大量的第…

WSL2编译使用6.6版本内核

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、有什么变化二、下载6.6内核三、开始编译1.安装环境2.开始编译 四、使用1.杀死虚拟机2.防止内核文件3.修改配置文件 总结 前言 最近出了一件不大不小的事&a…

【前端速通系列|第二篇】Vue3前置知识

文章目录 1.前言2.包管理工具npm2.1下载node.js2.2配置 npm 镜像源2.3 npm 常用命令 3.Vite构建工具4.Vue3组件化5.Vue3运行原理 1.前言 本系列文章旨在帮助大家快速上手前端开发。 2.包管理工具npm npm 是 node.js中进行 包管理 的工具. 类似于Java中的Maven。 2.1下载nod…

C++之goto陈述

关键字 goto用于控制程式执行的顺序&#xff0c;使程式直接跳到指定标签(lable) 的地方继续执行。 形式如下 标签可以是任意的识别字&#xff0c;后面接一个冒号。 举例如下 #include <iostream>int main() {goto label_one;label_one: {std::cout << "Lab…

第241题| 确定极限中参数问题 | 武忠祥老师每日一题

解题思路&#xff1a;确定极限中的参数的方法是求这个极限&#xff1b;求极限根据类型选方法。 形可以用到三种方法&#xff1a;洛必达&#xff0c;等价&#xff0c;泰勒。 先观察题目&#xff0c;将看成一个整体&#xff0c;同时,并令,整理之后如下&#xff1a; 这里也要想办…

如何使用uer做多分类任务

如何使用uer做多分类任务 语料集下载 找到这里点击即可 里面是这有json文件的 因此我们对此要做一些处理&#xff0c;将其转为tsv格式 # -*- coding: utf-8 -*- import json import csv import chardet# 检测文件编码 def detect_encoding(file_path):with open(file_path,…