【流媒体】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架构,封装的比较好)。

每个输出的转码过程,可以参考官网中给出的这个原理图:

  1. _______ ______________

  2. | | | |

  3. | 输入| 分路器| 编码数据| 解码器

  4. | 文件| ---------> | 数据包| ----- +

  5. | _______ | | ______________ | |

  6. v

  7. _________

  8. | |

  9. | 解码|

  10. | 框架|

  11. | _________ |

  12. ________ ______________ |

  13. | | | | |

  14. | 输出| <-------- | 编码数据| <---- +

  15. | 文件| muxer | 数据包| 编码器

  16. | ________ | | ______________ |

ffmpeg调用libavformat库(包含demuxers)来读取输入文件并获取包含编码数据的数据包。当有多个输入文件时,ffmpeg通过跟踪任何活动输入流上的最低时间戳,尝试使其保持同步。然后将编码的数据包传送给解码器(除非为数据流选择了流拷贝,请参阅进一步的描述)。解码器产生未压缩的帧(原始视频/ PCM音频/ ...),可以通过滤波进一步处理(见下一节)。在过滤之后,帧被传递给编码器,编码器对其进行编码并输出编码的数据包。最后,这些传递给复用器,将编码的数据包写入输出文件。

【java接口封装】

上面介绍了ffmpeg命令行的直接使用,在java程序中如果使用ffmpeg则需将命令封装为接口使用,一种简单的写法如下:

  1. package video;

  2. import java.io.File;

  3. import java.util.ArrayList;

  4. import java.util.Calendar;

  5. import java.util.List;

  6. public class ConvertH264 {

  7. private final static String PATH = "";

  8. //转码后的输出路径设置

  9. private final static String OUTPATH = "D:\\FfmpegFile\\output";

  10. public static void main(String[] args) {

  11. if(!checkfile(PATH)){

  12. System.out.println("源文件不是一个完整的文件,请检查");

  13. return;

  14. }

  15. process();

  16. }

  17. /**

  18. * 执行转码,期间对源视频是否被ffmpeg支持进行验证

  19. * @return 状态

  20. */

  21. private static int process(){

  22. int status = checkContentType();

  23. if(status == 0){

  24. processH264(PATH);

  25. }else if(status == 1){

  26. String tempPath = processAVI(PATH);

  27. if(!"ERROR".equals(tempPath)){

  28. processH264(tempPath);

  29. }

  30. }

  31. return status;

  32. }

  33. /**

  34. * 检查是否为一个完整的文件

  35. * @param path

  36. * @return

  37. */

  38. private static boolean checkfile(String path) {

  39. File file = new File(path);

  40. if (!file.isFile()) {

  41. return false;

  42. }

  43. return true;

  44. }

  45. /**

  46. * check contentType

  47. * @return int 0-能解析; 1-不能解析

  48. */

  49. private static int checkContentType() {

  50. String type = PATH.substring(PATH.lastIndexOf(".") + 1, PATH.length())

  51. .toLowerCase();

  52. //ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)

  53. if (type.equals("avi")) {

  54. return 0;

  55. } else if (type.equals("mpg")) {

  56. return 0;

  57. } else if (type.equals("wmv")) {

  58. return 0;

  59. } else if (type.equals("3gp")) {

  60. return 0;

  61. } else if (type.equals("mov")) {

  62. return 0;

  63. } else if (type.equals("mp4")) {

  64. return 0;

  65. } else if (type.equals("asf")) {

  66. return 0;

  67. } else if (type.equals("asx")) {

  68. return 0;

  69. } else if (type.equals("flv")) {

  70. return 0;

  71. }

  72. //对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),策略是先转换成avi格式

  73. else if (type.equals("wmv9")) {

  74. return 1;

  75. } else if (type.equals("rm")) {

  76. return 1;

  77. } else if (type.equals("rmvb")) {

  78. return 1;

  79. }

  80. return 2;

  81. }

  82. //暂时按照时间生成文件名(之后和史宏再商榷)

  83. private static String generateFileName(){

  84. Calendar c = Calendar.getInstance();

  85. return String.valueOf(c.getTimeInMillis())+ Math.round(Math.random() * 100000);

  86. }

  87. //注意ffmpeg一定要提前编译h264编码格式

  88. private static boolean processH264(String oldpath){

  89. //码率 -- 尺寸 -- 432*240 源帧率 -- 29 位率(继续调试,获得相对最清楚的版本)

  90. String savename = generateFileName();

  91. List<String> commend = new ArrayList<String>();

  92. //ffmpeg.exe的路径地址,下个版本,和程序地址同步,resource文件中

  93. commend.add("D:\\ffmpeg\\ffmpeg");

  94. commend.add("-i");

  95. commend.add(oldpath);

  96. commend.add("-ab");

  97. commend.add("56");

  98. commend.add("-ar");

  99. commend.add("22050");

  100. commend.add("-vcodec");

  101. commend.add("h264");

  102. commend.add("-qscale");

  103. commend.add("8");

  104. commend.add("-r");

  105. commend.add("15");

  106. commend.add("-s");

  107. commend.add("600*500");

  108. commend.add("D:\\" + savename + ".mp4");

  109. try {

  110. //调用线程命令进行转码

  111. ProcessBuilder builder = new ProcessBuilder(commend);

  112. builder.command(commend);

  113. builder.start();

  114. return true;

  115. } catch (Exception e) {

  116. e.printStackTrace();

  117. return false;

  118. }

  119. }

  120. private static String processAVI(String oldpath){

  121. String saveName = generateFileName();

  122. List<String> commend = new ArrayList<String>();

  123. commend.add("D\\ffmpeg\\mencoder");

  124. commend.add("-oac");

  125. commend.add("lavc");

  126. commend.add("-lavcopts");

  127. commend.add("acodec=mp3:abitrate=64");

  128. commend.add("-ovc");

  129. commend.add("xvid");

  130. commend.add("-xvidencopts");

  131. commend.add("bitrate=600");

  132. commend.add("-of");

  133. commend.add("avi");

  134. commend.add("-o");

  135. commend.add("D:\\FfmpegFile\\output" + saveName + ".avi");

  136. try{

  137. return OUTPATH + saveName + ".avi";

  138. }catch(Exception e){

  139. e.printStackTrace();

  140. return "ERROR";

  141. }

  142. }

  143. }

其中,核心代码段:

  1. ProcessBuilder builder = new ProcessBuilder(commend);

  2. builder.command(commend);

  3. builder.start();

通过启动一个新的进程,来让他进行转码,而这个进程,其实就是dos命令的方式去调用ffmpeg。最初使用这种方式实现转码功能,发现问题在于转码过程中进程死掉了,转码一半的时候失败了,程序中也不能发现,进程异步执行,不可控。

之后,在github上,发现流媒体大神的ffmpeg命令接口化工具:

https://github.com/eguid/FFmpegCommandHandler4java

这个开源项目会在以后单独介绍分享,把ffmpeg在java中接口的封装做到了可控、命令执行、停止、查询的功能。基本功能使用:

  1. FFmpegManager manager=new FFmpegManagerImpl(10);

  2. //当然也可以这样:FFmpegManager manager=new FFmpegManagerImpl();//这样会从配置文件中读取size的值作为初始化参数

  3. //组装命令

  4. Map map = new HashMap();

  5. map.put("appName", "test123");

  6. map.put("input","rtsp://admin:admin@192.168.2.236:37779/cam/realmonitor?channel=1&subtype=0");

  7. map.put("output", "rtmp://192.168.30.21/live/");

  8. map.put("codec","h264");

  9. map.put("fmt", "flv");

  10. map.put("fps", "25");

  11. map.put("rs", "640x360");

  12. map.put("twoPart","2");

  13. //执行任务,id就是appName,如果执行失败返回为null

  14. String id=manager.start(map);

  15. System.out.println(id);

  16. //通过id查询

  17. TaskEntity info=manager.query(id);

  18. System.out.println(info);

  19. //查询全部

  20. Collection<TaskEntity> infoList=manager.queryAll();

  21. System.out.println(infoList);

  22. //停止id对应的任务

  23. manager.stop(id);

  24. //执行原生ffmpeg命令(不包含ffmpeg的执行路径,该路径会从配置文件中自动读取)

  25. manager.start("test1", "ffmpeg -i input_file -vcodec copy -an output_file_video");

  26. //包含完整ffmpeg执行路径的命令

  27. manager.start("test2,","d:/ffmpeg/ffmpeg -i input_file -vcodec copy -an output_file_video",true);

  28. //停止全部任务

  29. manager.stopAll();

【jave】

这里简单提一下,jave是利用ffmpeg封装的一个java控制的流媒体工具包,地址:

http://www.sauronsoftware.it/projects/jave/

比较遗憾的一点是,jave在09年之后,就停止了更新,利用jave实现视频编码格式转换的核心代码如下:

  1. private String process(String oldpath){

  2. String newPath = "";

  3. try {

  4. newPath = TEMP_FILE_PATH + File.separator + System.currentTimeMillis() + ".mp4";

  5. File source = new File(oldpath);

  6. File target = new File(newPath);

  7. AudioAttributes audio = new AudioAttributes();

  8. audio.setCodec("libmp3lame");

  9. VideoAttributes video = new VideoAttributes();

  10. video.setCodec("flv");

  11. video.setBitRate(new Integer(360000));

  12. video.setFrameRate(new Integer(30));

  13. video.setSize(new VideoSize(400, 300));

  14. EncodingAttributes attrs = new EncodingAttributes();

  15. attrs.setFormat("flv");

  16. attrs.setAudioAttributes(audio);

  17. attrs.setVideoAttributes(video);

  18. Encoder encoder = new Encoder();

  19. //获取编码信息

  20. MultimediaInfo beforeStatus = encoder.getInfo(source);

  21. encoder.encode(source, target, attrs);

  22. //通过转后的状态判断

  23. if(!target.exists() || target.length() == 0){

  24. return "";

  25. }

  26. } catch (IllegalArgumentException e) {

  27. logger.error("转码encode过程中出现异常",e);

  28. return "";

  29. } catch (InputFormatException e) {

  30. logger.error("转码encode过程中出现异常",e);

  31. return "";

  32. } catch (EncoderException e) {

  33. logger.error("转码encode过程中出现异常",e);

  34. return "";

  35. }

  36. return newPath;

  37. }

处理的完整过程,请参考我的github下的VideoEncoder项目:

https://github.com/zhangzhenhua92/VideoEncoder

之后两篇博客,将分别从jave和ffmpeg命令管理器两个方向,继续分享上个月的流媒体之旅。

音视频开发(3)---ffmpeg相关推荐

  1. Android音视频开发从入门到精通,我这一路走来的经验分享

    前不久,在国家统计局针对北京市进行的农民工市民化状况进行的调研中,从事信息技术,软件技术等IT服务业的人员也被当做农民工. 编程的门槛不高,薪资水平也还可观.这一直是"三百六十行,行行转IT ...

  2. ffmpeg获取设备支持的分辨率_Qt音视频开发6-ffmpeg解码处理

    一.前言 采用ffmpeg解码,是所有视频监控开发人员必备的技能,绕不过去的一个玩意,甚至可以说是所有音视频开发人员的必备技能.FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开 ...

  3. ffmpeg前景_5G时代音视频开发前景怎么样?音视频开发需要掌握哪些技术?

    前言 从历史来看,2G打开了了移动互联网天下,3G带来了即时通信,诞生了QQ 微信等巨头,4G 带来了短视频兴起.字节跳动等公司崛起.2 3 4G的出现促成了移动互联网10年繁荣.而5G的出现,也会促 ...

  4. FFmpeg 工具:音视频开发都用它,快@你兄弟来看丨音视频工具

    (本文基本逻辑:ffmpeg 常用命令介绍 → ffplay 常用命令介绍 → ffprobe 常用命令介绍) 从事音视频开发的程序员几乎都应该知道或使用过 FFmpeg.FFmpeg 是一个开源软件 ...

  5. 音视频开发之旅(34) - 基于FFmpeg实现简单的视频解码器

    目录 FFmpeg解码过程流程图和关键的数据结构 mp4通过FFmpeg解码YUV裸视频数据 遇到的问题 资料 收获 一.FFmpeg解码过程流程图和关键的数据结构 FFmpeg解码涉及的知识点比较多 ...

  6. 音视频开发系列(10)ffmpeg基础使用

    一.安装 打开https://ffmpeg.zeranoe.com/builds/,该网站中的FFMPEG分为3个版本:Static,Shared,Dev. 前两个版本可以直接在命令行中使用,他们的区 ...

  7. JavaCV音视频开发宝典:使用JavaCV采集windows系统声音并录制成mp3音频文件(FFmpeg采集windows系统声音)

    <JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 之前已经写过很多采集windows.macos和linux本机麦克风声音的文章, ...

  8. FFmpeg 音视频开发 20 年

    感谢小编邀请,让我写下 FFmpeg 20 年这么有历史厚重的话题. 写文章其实比录视频教程要求高很多,要字斟句酌,逻辑严密,理论知识严谨.由于个人文笔实在有限,长期以来,不敢随便写文章,更不敢出书, ...

  9. iOS 音视频开发:Audio Unit播放FFmpeg解码的音频

    本文档描述了iOS播放经FFmpeg解码的音频数据的编程步骤,具体基于Audio Toolbox框架的Audio Session和Audio Unit框架提供的接口实现.在iOS 7及以上平台Audi ...

  10. 音视频开发成长之路—进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频

    音视频开发成长之路-进阶之路3个重要知识点 视频讲解如下,点击观看: 音视频开发成长之路-进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频 音视频高级 ...

最新文章

  1. telnet给服务器发消息,Telnet按字符发送字符串
  2. 考前自学系列·计算机组成原理·计算机的硬件组成及其功能
  3. 【贪心】【字典树】Gym - 101466A - Gaby And Addition
  4. Java 获取本机IP和Mac以及网卡信息
  5. spss process插件_SPSS教程:绘制调节效应图
  6. amvu mysql_mysql--数据库备份
  7. 局域网内多台linux服务器时间同步的一种解决方案
  8. [蓝桥杯][2014年第五届真题]兰顿蚂蚁(模拟)
  9. 关系数据库的几种设计范式介绍
  10. mysql学习笔记 查找技术 1207 0311
  11. c# 封装“度分秒”与弧度之间的转换 以及datagridview控件的应用
  12. 百度网盘自动备份php,服务器自动备份脚本上传至百度云存储
  13. 知物由学 |“网状世界”下,无处可逃的信息安全
  14. JQuery的Ajax跨域请求的解决方案
  15. linux下,如何迁移mysql数据库存放目录。利用软连接简单快捷实现移动到home数据盘下...
  16. mysql++缓冲区_思考mysql内核之初级系列4--innodb缓冲区管理(摘自老杨)
  17. Mysql8.0 15安装后怎么打开_最新最全mysql8.0.15安装配置及连接Navicat教程
  18. 阿里视频播放vodPlayer.setMuteMode(true) 设置静音失效的解决办法
  19. 大学生安卓期末设计之本地音乐播放器
  20. 武汉理工计算机网络教学平台,武汉理工大学 操作系统

热门文章

  1. python 矩阵类型转换_Python3 列表,数组,矩阵的相互转换的方法示例
  2. Leetcode - 143. Reorder List
  3. php unset数组,php unset 数组不管用
  4. 通讯故障_PLC与变频器通讯故障处理实例
  5. JAVA异常-面试题
  6. css多行文本溢出显示省略号(兼容ie)
  7. Press ^C at any time to quit.
  8. 9.25-CSS样式以及结构布局
  9. jQuery选择器之id选择器
  10. C++第二次上机5-5