音视频开发(3)---ffmpeg
【流媒体】ffmpeg
【前提】
java本身没有自己的流媒体架构,而且没有公司和人在为java开发一套流媒体架构,就连nginx-rtmp和srs这种主流级别的流媒体服务器都在使用ffmpeg做插件,可见ffmpeg在流媒体架构这块的重要性。
【宏观】
官方地址:https://ffmpeg.org/
画了2幅图,简单对ffmpeg这个流媒体架构做个简单的说明:
图 1-0 ffmpeg基本信息
图1-1 ffmpeg四大功能、库文件
两幅图,大致可以了解到,ffmpeg是一套处理流媒体的开源、免费架构,对于java后台开发来讲,如若遇到流媒体处理需求,无论使用何种架构,其实本质上是绕不开ffmpeg的,常用的“暴风影音”、“qq影音”、“迅雷影音”等,打开他们的安装目录,其实都会发现ffmpeg的身影,可见其强大之处。
目前,ffmpeg的几大功能点:1.编解码、2.转码、3.复用、4.流处理、5.过滤、6.播放,我主要接触“编解码”和“转码”,具体可以参考我的上一篇博客:【视频】“异步+准实时”解决主流H5播放器格式兼容问题 , 主要用的是ffmpeg的命令行功能,将其封装为java后台接口,实现转码操作,接下来主要从“ffmpeg命令行”使用、java后台封装2个角度进行ffmpeg的介绍,之后有需求,会对“ffserver”、“ffplayer”、“ffprobe”进行介绍。
【命令行工具】
ffmpeg [ global_options ] {[ input_file_options ] -iinput_url} ... {[ output_file_options ]output_url} ...
如上所述,这是ffmpeg命令行的基本格式,快速的视频和音频转换器,也可以从现场音频/视频源获取。它还可以在任意采样率之间转换,并通过高质量的多相滤波器实时调整视频大小。
举几个简单的例子:
(1)该命令要将输出文件的视频比特率设置为64 kbit / s
ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi
(2)要强制输出文件的帧频为24 fps
ffmpeg -i input.avi -r 24 output.avi
(3)要强制输入文件的帧速率(仅适用于原始格式)为1 fps,输出文件的帧速率为24 fps:
ffmpeg -r 1 -i input.m2v -r 24 output.avi
(4)将avi格式视频A转化为flv格式视频
ffmpeg -i video_origine.avi -acodec libmp3lame -ab 56K -ar 44100 -b 200K -r 15 -s 320x240 -f flv video_finale.flv
(5)分离视频中的音频流
ffmpeg -i input_file -vcodec copy -an output_file_video
如上所述,是直接利用ffmpeg命令行来操作视频的过程,其中1.2.3是改变原有视频的帧率、码率等视频原有特性,4是将视频的编码格式进行转码(pay attention:我们说的格式,在上一篇文章中也提到了,文件格式、视频格式、视频编码格式,这里讨论的是最后一种),5则是将视频进行提取、分离。
在windows或者Linux不同的环境下使用ffmpeg,需要下载不同的安装包:https://ffmpeg.org/download.html,比如在转码过程,会是酱紫:
基本上转码过程和“格式工厂”效率差不多,转后的清晰度,则是由我们设置的帧率和码率来控制,ffmpeg做视频转码,有一点不足,就是比较消耗CPU资源,本机8GB内存,如图:
性能会直接飙升至90%,不过好在问题是在之后的jave架构中被解决(jave本质也是ffmpeg架构,封装的比较好)。
每个输出的转码过程,可以参考官网中给出的这个原理图:
_______ ______________
| | | |
| 输入| 分路器| 编码数据| 解码器
| 文件| ---------> | 数据包| ----- +
| _______ | | ______________ | |
v
_________
| |
| 解码|
| 框架|
| _________ |
________ ______________ |
| | | | |
| 输出| <-------- | 编码数据| <---- +
| 文件| muxer | 数据包| 编码器
| ________ | | ______________ |
ffmpeg调用libavformat库(包含demuxers)来读取输入文件并获取包含编码数据的数据包。当有多个输入文件时,ffmpeg通过跟踪任何活动输入流上的最低时间戳,尝试使其保持同步。然后将编码的数据包传送给解码器(除非为数据流选择了流拷贝,请参阅进一步的描述)。解码器产生未压缩的帧(原始视频/ PCM音频/ ...),可以通过滤波进一步处理(见下一节)。在过滤之后,帧被传递给编码器,编码器对其进行编码并输出编码的数据包。最后,这些传递给复用器,将编码的数据包写入输出文件。
【java接口封装】
上面介绍了ffmpeg命令行的直接使用,在java程序中如果使用ffmpeg则需将命令封装为接口使用,一种简单的写法如下:
package video;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class ConvertH264 {
private final static String PATH = "";
//转码后的输出路径设置
private final static String OUTPATH = "D:\\FfmpegFile\\output";
public static void main(String[] args) {
if(!checkfile(PATH)){
System.out.println("源文件不是一个完整的文件,请检查");
return;
}
process();
}
/**
* 执行转码,期间对源视频是否被ffmpeg支持进行验证
* @return 状态
*/
private static int process(){
int status = checkContentType();
if(status == 0){
processH264(PATH);
}else if(status == 1){
String tempPath = processAVI(PATH);
if(!"ERROR".equals(tempPath)){
processH264(tempPath);
}
}
return status;
}
/**
* 检查是否为一个完整的文件
* @param path
* @return
*/
private static boolean checkfile(String path) {
File file = new File(path);
if (!file.isFile()) {
return false;
}
return true;
}
/**
* check contentType
* @return int 0-能解析; 1-不能解析
*/
private static int checkContentType() {
String type = PATH.substring(PATH.lastIndexOf(".") + 1, PATH.length())
.toLowerCase();
//ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
if (type.equals("avi")) {
return 0;
} else if (type.equals("mpg")) {
return 0;
} else if (type.equals("wmv")) {
return 0;
} else if (type.equals("3gp")) {
return 0;
} else if (type.equals("mov")) {
return 0;
} else if (type.equals("mp4")) {
return 0;
} else if (type.equals("asf")) {
return 0;
} else if (type.equals("asx")) {
return 0;
} else if (type.equals("flv")) {
return 0;
}
//对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),策略是先转换成avi格式
else if (type.equals("wmv9")) {
return 1;
} else if (type.equals("rm")) {
return 1;
} else if (type.equals("rmvb")) {
return 1;
}
return 2;
}
//暂时按照时间生成文件名(之后和史宏再商榷)
private static String generateFileName(){
Calendar c = Calendar.getInstance();
return String.valueOf(c.getTimeInMillis())+ Math.round(Math.random() * 100000);
}
//注意ffmpeg一定要提前编译h264编码格式
private static boolean processH264(String oldpath){
//码率 -- 尺寸 -- 432*240 源帧率 -- 29 位率(继续调试,获得相对最清楚的版本)
String savename = generateFileName();
List<String> commend = new ArrayList<String>();
//ffmpeg.exe的路径地址,下个版本,和程序地址同步,resource文件中
commend.add("D:\\ffmpeg\\ffmpeg");
commend.add("-i");
commend.add(oldpath);
commend.add("-ab");
commend.add("56");
commend.add("-ar");
commend.add("22050");
commend.add("-vcodec");
commend.add("h264");
commend.add("-qscale");
commend.add("8");
commend.add("-r");
commend.add("15");
commend.add("-s");
commend.add("600*500");
commend.add("D:\\" + savename + ".mp4");
try {
//调用线程命令进行转码
ProcessBuilder builder = new ProcessBuilder(commend);
builder.command(commend);
builder.start();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static String processAVI(String oldpath){
String saveName = generateFileName();
List<String> commend = new ArrayList<String>();
commend.add("D\\ffmpeg\\mencoder");
commend.add("-oac");
commend.add("lavc");
commend.add("-lavcopts");
commend.add("acodec=mp3:abitrate=64");
commend.add("-ovc");
commend.add("xvid");
commend.add("-xvidencopts");
commend.add("bitrate=600");
commend.add("-of");
commend.add("avi");
commend.add("-o");
commend.add("D:\\FfmpegFile\\output" + saveName + ".avi");
try{
return OUTPATH + saveName + ".avi";
}catch(Exception e){
e.printStackTrace();
return "ERROR";
}
}
}
其中,核心代码段:
ProcessBuilder builder = new ProcessBuilder(commend);
builder.command(commend);
builder.start();
通过启动一个新的进程,来让他进行转码,而这个进程,其实就是dos命令的方式去调用ffmpeg。最初使用这种方式实现转码功能,发现问题在于转码过程中进程死掉了,转码一半的时候失败了,程序中也不能发现,进程异步执行,不可控。
之后,在github上,发现流媒体大神的ffmpeg命令接口化工具:
https://github.com/eguid/FFmpegCommandHandler4java
这个开源项目会在以后单独介绍分享,把ffmpeg在java中接口的封装做到了可控、命令执行、停止、查询的功能。基本功能使用:
FFmpegManager manager=new FFmpegManagerImpl(10);
//当然也可以这样:FFmpegManager manager=new FFmpegManagerImpl();//这样会从配置文件中读取size的值作为初始化参数
//组装命令
Map map = new HashMap();
map.put("appName", "test123");
map.put("input","rtsp://admin:admin@192.168.2.236:37779/cam/realmonitor?channel=1&subtype=0");
map.put("output", "rtmp://192.168.30.21/live/");
map.put("codec","h264");
map.put("fmt", "flv");
map.put("fps", "25");
map.put("rs", "640x360");
map.put("twoPart","2");
//执行任务,id就是appName,如果执行失败返回为null
String id=manager.start(map);
System.out.println(id);
//通过id查询
TaskEntity info=manager.query(id);
System.out.println(info);
//查询全部
Collection<TaskEntity> infoList=manager.queryAll();
System.out.println(infoList);
//停止id对应的任务
manager.stop(id);
//执行原生ffmpeg命令(不包含ffmpeg的执行路径,该路径会从配置文件中自动读取)
manager.start("test1", "ffmpeg -i input_file -vcodec copy -an output_file_video");
//包含完整ffmpeg执行路径的命令
manager.start("test2,","d:/ffmpeg/ffmpeg -i input_file -vcodec copy -an output_file_video",true);
//停止全部任务
manager.stopAll();
【jave】
这里简单提一下,jave是利用ffmpeg封装的一个java控制的流媒体工具包,地址:
http://www.sauronsoftware.it/projects/jave/
比较遗憾的一点是,jave在09年之后,就停止了更新,利用jave实现视频编码格式转换的核心代码如下:
private String process(String oldpath){
String newPath = "";
try {
newPath = TEMP_FILE_PATH + File.separator + System.currentTimeMillis() + ".mp4";
File source = new File(oldpath);
File target = new File(newPath);
AudioAttributes audio = new AudioAttributes();
audio.setCodec("libmp3lame");
VideoAttributes video = new VideoAttributes();
video.setCodec("flv");
video.setBitRate(new Integer(360000));
video.setFrameRate(new Integer(30));
video.setSize(new VideoSize(400, 300));
EncodingAttributes attrs = new EncodingAttributes();
attrs.setFormat("flv");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);
Encoder encoder = new Encoder();
//获取编码信息
MultimediaInfo beforeStatus = encoder.getInfo(source);
encoder.encode(source, target, attrs);
//通过转后的状态判断
if(!target.exists() || target.length() == 0){
return "";
}
} catch (IllegalArgumentException e) {
logger.error("转码encode过程中出现异常",e);
return "";
} catch (InputFormatException e) {
logger.error("转码encode过程中出现异常",e);
return "";
} catch (EncoderException e) {
logger.error("转码encode过程中出现异常",e);
return "";
}
return newPath;
}
处理的完整过程,请参考我的github下的VideoEncoder项目:
https://github.com/zhangzhenhua92/VideoEncoder
之后两篇博客,将分别从jave和ffmpeg命令管理器两个方向,继续分享上个月的流媒体之旅。
音视频开发(3)---ffmpeg相关推荐
- Android音视频开发从入门到精通,我这一路走来的经验分享
前不久,在国家统计局针对北京市进行的农民工市民化状况进行的调研中,从事信息技术,软件技术等IT服务业的人员也被当做农民工. 编程的门槛不高,薪资水平也还可观.这一直是"三百六十行,行行转IT ...
- ffmpeg获取设备支持的分辨率_Qt音视频开发6-ffmpeg解码处理
一.前言 采用ffmpeg解码,是所有视频监控开发人员必备的技能,绕不过去的一个玩意,甚至可以说是所有音视频开发人员的必备技能.FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开 ...
- ffmpeg前景_5G时代音视频开发前景怎么样?音视频开发需要掌握哪些技术?
前言 从历史来看,2G打开了了移动互联网天下,3G带来了即时通信,诞生了QQ 微信等巨头,4G 带来了短视频兴起.字节跳动等公司崛起.2 3 4G的出现促成了移动互联网10年繁荣.而5G的出现,也会促 ...
- FFmpeg 工具:音视频开发都用它,快@你兄弟来看丨音视频工具
(本文基本逻辑:ffmpeg 常用命令介绍 → ffplay 常用命令介绍 → ffprobe 常用命令介绍) 从事音视频开发的程序员几乎都应该知道或使用过 FFmpeg.FFmpeg 是一个开源软件 ...
- 音视频开发之旅(34) - 基于FFmpeg实现简单的视频解码器
目录 FFmpeg解码过程流程图和关键的数据结构 mp4通过FFmpeg解码YUV裸视频数据 遇到的问题 资料 收获 一.FFmpeg解码过程流程图和关键的数据结构 FFmpeg解码涉及的知识点比较多 ...
- 音视频开发系列(10)ffmpeg基础使用
一.安装 打开https://ffmpeg.zeranoe.com/builds/,该网站中的FFMPEG分为3个版本:Static,Shared,Dev. 前两个版本可以直接在命令行中使用,他们的区 ...
- JavaCV音视频开发宝典:使用JavaCV采集windows系统声音并录制成mp3音频文件(FFmpeg采集windows系统声音)
<JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 之前已经写过很多采集windows.macos和linux本机麦克风声音的文章, ...
- FFmpeg 音视频开发 20 年
感谢小编邀请,让我写下 FFmpeg 20 年这么有历史厚重的话题. 写文章其实比录视频教程要求高很多,要字斟句酌,逻辑严密,理论知识严谨.由于个人文笔实在有限,长期以来,不敢随便写文章,更不敢出书, ...
- iOS 音视频开发:Audio Unit播放FFmpeg解码的音频
本文档描述了iOS播放经FFmpeg解码的音频数据的编程步骤,具体基于Audio Toolbox框架的Audio Session和Audio Unit框架提供的接口实现.在iOS 7及以上平台Audi ...
- 音视频开发成长之路—进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频
音视频开发成长之路-进阶之路3个重要知识点 视频讲解如下,点击观看: 音视频开发成长之路-进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频 音视频高级 ...
最新文章
- telnet给服务器发消息,Telnet按字符发送字符串
- 考前自学系列·计算机组成原理·计算机的硬件组成及其功能
- 【贪心】【字典树】Gym - 101466A - Gaby And Addition
- Java 获取本机IP和Mac以及网卡信息
- spss process插件_SPSS教程:绘制调节效应图
- amvu mysql_mysql--数据库备份
- 局域网内多台linux服务器时间同步的一种解决方案
- [蓝桥杯][2014年第五届真题]兰顿蚂蚁(模拟)
- 关系数据库的几种设计范式介绍
- mysql学习笔记 查找技术 1207 0311
- c# 封装“度分秒”与弧度之间的转换 以及datagridview控件的应用
- 百度网盘自动备份php,服务器自动备份脚本上传至百度云存储
- 知物由学 |“网状世界”下,无处可逃的信息安全
- JQuery的Ajax跨域请求的解决方案
- linux下,如何迁移mysql数据库存放目录。利用软连接简单快捷实现移动到home数据盘下...
- mysql++缓冲区_思考mysql内核之初级系列4--innodb缓冲区管理(摘自老杨)
- Mysql8.0 15安装后怎么打开_最新最全mysql8.0.15安装配置及连接Navicat教程
- 阿里视频播放vodPlayer.setMuteMode(true) 设置静音失效的解决办法
- 大学生安卓期末设计之本地音乐播放器
- 武汉理工计算机网络教学平台,武汉理工大学 操作系统