• Java通过javacv获取视频、音频、图片等元数据信息(分辨率、大小、帧等信息)


    相信我们都会或多或少需要给前端返回视频或者音频的一些信息,那么今天这篇文章通过Java语言使用javacv来获取视频、音频、图片等元数据信息(分辨率、大小、帧等信息)

    一、首先导入依赖

    可以先导入javacv/javacv-platform依赖,由于依赖比较大,所以我们可以先去除部分不需要的依赖如下:

    1. <dependency>
    2. <groupId>org.bytedeco</groupId>
    3. <artifactId>javacv</artifactId>
    4. <version>1.4.4</version>
    5. <exclusions>
    6. <exclusion>
    7. <groupId>org.bytedeco</groupId>
    8. <artifactId>javacpp</artifactId>
    9. </exclusion>
    10. <exclusion>
    11. <groupId>org.bytedeco.javacpp-presets</groupId>
    12. <artifactId>flycapture</artifactId>
    13. </exclusion>
    14. <exclusion>
    15. <groupId>org.bytedeco.javacpp-presets</groupId>
    16. <artifactId>libdc1394</artifactId>
    17. </exclusion>
    18. <exclusion>
    19. <groupId>org.bytedeco.javacpp-presets</groupId>
    20. <artifactId>libfreenect</artifactId>
    21. </exclusion>
    22. <exclusion>
    23. <groupId>org.bytedeco.javacpp-presets</groupId>
    24. <artifactId>libfreenect2</artifactId>
    25. </exclusion>
    26. <exclusion>
    27. <groupId>org.bytedeco.javacpp-presets</groupId>
    28. <artifactId>librealsense</artifactId>
    29. </exclusion>
    30. <exclusion>
    31. <groupId>org.bytedeco.javacpp-presets</groupId>
    32. <artifactId>videoinput</artifactId>
    33. </exclusion>
    34. <exclusion>
    35. <groupId>org.bytedeco.javacpp-presets</groupId>
    36. <artifactId>opencv</artifactId>
    37. </exclusion>
    38. <exclusion>
    39. <groupId>org.bytedeco.javacpp-presets</groupId>
    40. <artifactId>tesseract</artifactId>
    41. </exclusion>
    42. <exclusion>
    43. <groupId>org.bytedeco.javacpp-presets</groupId>
    44. <artifactId>leptonica</artifactId>
    45. </exclusion>
    46. <exclusion>
    47. <groupId>org.bytedeco.javacpp-presets</groupId>
    48. <artifactId>flandmark</artifactId>
    49. </exclusion>
    50. <exclusion>
    51. <groupId>org.bytedeco.javacpp-presets</groupId>
    52. <artifactId>artoolkitplus</artifactId>
    53. </exclusion>
    54. </exclusions>
    55. </dependency>
    56. <dependency>
    57. <groupId>org.bytedeco</groupId>
    58. <artifactId>javacv-platform</artifactId>
    59. <version>1.4.4</version>
    60. <exclusions>
    61. <exclusion>
    62. <groupId>org.bytedeco</groupId>
    63. <artifactId>javacv</artifactId>
    64. </exclusion>
    65. <exclusion>
    66. <groupId>org.bytedeco.javacpp-presets</groupId>
    67. <artifactId>flycapture-platform</artifactId>
    68. </exclusion>
    69. <exclusion>
    70. <groupId>org.bytedeco.javacpp-presets</groupId>
    71. <artifactId>libdc1394-platform</artifactId>
    72. </exclusion>
    73. <exclusion>
    74. <groupId>org.bytedeco.javacpp-presets</groupId>
    75. <artifactId>libfreenect-platform</artifactId>
    76. </exclusion>
    77. <exclusion>
    78. <groupId>org.bytedeco.javacpp-presets</groupId>
    79. <artifactId>libfreenect2-platform</artifactId>
    80. </exclusion>
    81. <exclusion>
    82. <groupId>org.bytedeco.javacpp-presets</groupId>
    83. <artifactId>librealsense-platform</artifactId>
    84. </exclusion>
    85. <exclusion>
    86. <groupId>org.bytedeco.javacpp-presets</groupId>
    87. <artifactId>videoinput-platform</artifactId>
    88. </exclusion>
    89. <exclusion>
    90. <groupId>org.bytedeco.javacpp-presets</groupId>
    91. <artifactId>opencv-platform</artifactId>
    92. </exclusion>
    93. <exclusion>
    94. <groupId>org.bytedeco.javacpp-presets</groupId>
    95. <artifactId>tesseract-platform</artifactId>
    96. </exclusion>
    97. <exclusion>
    98. <groupId>org.bytedeco.javacpp-presets</groupId>
    99. <artifactId>leptonica-platform</artifactId>
    100. </exclusion>
    101. <exclusion>
    102. <groupId>org.bytedeco.javacpp-presets</groupId>
    103. <artifactId>flandmark-platform</artifactId>
    104. </exclusion>
    105. <exclusion>
    106. <groupId>org.bytedeco.javacpp-presets</groupId>
    107. <artifactId>artoolkitplus-platform</artifactId>
    108. </exclusion>
    109. </exclusions>
    110. </dependency>

    二、获取视频、音频或图片实体元信息

    FFmpegUtil实体
    1. @Slf4j
    2. @Service
    3. public class FFmpegUtil {
    4. private static final String IMAGE_SUFFIX = "png";
    5. /**
    6. * 获取视频时长,单位为秒S
    7. * @param file 视频源
    8. * @return
    9. */
    10. @SuppressWarnings("resource")
    11. public static long videoDuration(File file) {
    12. long duration = 0L;
    13. FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
    14. try {
    15. ff.start();
    16. duration = ff.getLengthInTime() / (1000 * 1000);
    17. ff.stop();
    18. } catch (java.lang.Exception e) {
    19. e.printStackTrace();
    20. }
    21. return duration;
    22. }
    23. /**
    24. * 获取视频帧图片
    25. * @param file 视频源
    26. * @param number 第几帧
    27. * @param dir 文件存放根目录
    28. * @param args 文件存放根目录
    29. * @return
    30. */
    31. @SuppressWarnings("resource")
    32. public static String videoImage(File file, Integer number, String dir, String args) {
    33. String picPath = StringUtils.EMPTY;
    34. FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
    35. try {
    36. ff.start();
    37. int i = 0;
    38. int length = ff.getLengthInFrames();
    39. Frame frame = null;
    40. while (i < length) {
    41. frame = ff.grabFrame();
    42. //截取第几帧图片
    43. if ((i > number) && (frame.image != null)) {
    44. //获取生成图片的路径
    45. picPath = getImagePath(args);
    46. //执行截图并放入指定位置
    47. doExecuteFrame(frame, dir + File.separator + picPath);
    48. break;
    49. }
    50. i++;
    51. }
    52. ff.stop();
    53. } catch (FrameGrabber.Exception e) {
    54. e.printStackTrace();
    55. }
    56. return picPath;
    57. }
    58. /**
    59. * 截取缩略图
    60. * @param frame
    61. * @param targerFilePath 图片存放路径
    62. */
    63. public static void doExecuteFrame(Frame frame, String targerFilePath) {
    64. //截取的图片
    65. if (null == frame || null == frame.image) {
    66. return;
    67. }
    68. Java2DFrameConverter converter = new Java2DFrameConverter();
    69. BufferedImage srcImage = converter.getBufferedImage(frame);
    70. int srcImageWidth = srcImage.getWidth();
    71. int srcImageHeight = srcImage.getHeight();
    72. //对帧图片进行等比例缩放(缩略图)
    73. int width = 480;
    74. int height = (int) (((double) width / srcImageWidth) * srcImageHeight);
    75. BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    76. thumbnailImage.getGraphics().drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
    77. File output = new File(targerFilePath);
    78. try {
    79. ImageIO.write(thumbnailImage, IMAGE_SUFFIX, output);
    80. } catch (IOException e) {
    81. e.printStackTrace();
    82. }
    83. }
    84. /**
    85. * 生成图片的相对路径
    86. * @param args 传入生成图片的名称、为空则用UUID命名
    87. * @return 例如 upload/images.png
    88. */
    89. public static String getImagePath(String args) {
    90. if (StringUtils.isNotEmpty(args)) {
    91. return args + "." + IMAGE_SUFFIX;
    92. }
    93. return getUUID() + "." + IMAGE_SUFFIX;
    94. }
    95. /**
    96. * 生成唯一的uuid
    97. * @return uuid
    98. */
    99. public static String getUUID(){
    100. return UUID.randomUUID().toString().replace("-","");
    101. }
    102. /**
    103. * 时长格式换算
    104. * @param duration 时长
    105. * @return HH:mm:ss
    106. */
    107. public static String formatDuration(Long duration) {
    108. String formatTime = StringUtils.EMPTY;
    109. double time = Double.valueOf(duration);
    110. if (time > -1) {
    111. int hour = (int) Math.floor(time / 3600);
    112. int minute = (int) (Math.floor(time / 60) % 60);
    113. int second = (int) (time % 60);
    114. if (hour < 10) {
    115. formatTime = "0";
    116. }
    117. formatTime += hour + ":";
    118. if (minute < 10) {
    119. formatTime += "0";
    120. }
    121. formatTime += minute + ":";
    122. if (second < 10) {
    123. formatTime += "0";
    124. }
    125. formatTime += second;
    126. }
    127. return formatTime;
    128. }
    129. /**
    130. * 获取图片大小Kb
    131. * @param urlPath
    132. * @return
    133. */
    134. public static String getImageSize(String urlPath){
    135. // 得到数据
    136. byte[] imageFromURL = getImageFromURL(urlPath);
    137. // 转换
    138. String byte2kb = bytes2kb(imageFromURL.length);
    139. return byte2kb;
    140. }
    141. /**
    142. * 根据图片地址获取图片信息
    143. *
    144. * @param urlPath 网络图片地址
    145. * @return
    146. */
    147. public static byte[] getImageFromURL(String urlPath) {
    148. // 字节数组
    149. byte[] data = null;
    150. // 输入流
    151. InputStream is = null;
    152. // Http连接对象
    153. HttpURLConnection conn = null;
    154. try {
    155. // Url对象
    156. URL url = new URL(urlPath);
    157. // 打开连接
    158. conn = (HttpURLConnection) url.openConnection();
    159. // 打开读取 写入是setDoOutput
    160. // conn.setDoOutput(true);
    161. conn.setDoInput(true);
    162. // 设置请求方式
    163. conn.setRequestMethod("GET");
    164. // 设置超时时间
    165. conn.setConnectTimeout(6000);
    166. // 得到访问的数据流
    167. is = conn.getInputStream();
    168. // 验证访问状态是否是200 正常
    169. if (conn.getResponseCode() == 200) {
    170. data = readInputStream(is);
    171. } else {
    172. data = null;
    173. }
    174. } catch (MalformedURLException e) {
    175. e.printStackTrace();
    176. } catch (IOException e) {
    177. e.printStackTrace();
    178. } finally {
    179. try {
    180. if (is != null) {
    181. // 关闭流
    182. is.close();
    183. }
    184. } catch (IOException e) {
    185. e.printStackTrace();
    186. }
    187. // 关闭连接
    188. conn.disconnect();
    189. }
    190. return data;
    191. }
    192. /**
    193. * 将流转换为字节
    194. *
    195. * @param is
    196. * @return
    197. */
    198. public static byte[] readInputStream(InputStream is) {
    199. /**
    200. * 捕获内存缓冲区的数据,转换成字节数组
    201. * ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区,然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出byte型数据。
    202. * 在网络传输中我们往往要传输很多变量,我们可以利用ByteArrayOutputStream把所有的变量收集到一起,然后一次性把数据发送出去。
    203. */
    204. ByteArrayOutputStream baos = new ByteArrayOutputStream();
    205. // 创建字节数组 1024 = 1M
    206. byte[] buffer = new byte[1024];
    207. // 防止无限循环
    208. int length = -1;
    209. try {
    210. // 循环写入数据到字节数组
    211. while ((length = is.read(buffer)) != -1) {
    212. baos.write(buffer, 0, length);
    213. }
    214. // 强制刷新,扫尾工作,主要是为了,让数据流在管道的传输工程中全部传输过去,防止丢失数据
    215. baos.flush();
    216. } catch (IOException e) {
    217. e.printStackTrace();
    218. }
    219. // 字节流转换字节数组
    220. byte[] data = baos.toByteArray();
    221. try {
    222. // 关闭读取流
    223. is.close();
    224. // 关闭写入流
    225. baos.close();
    226. } catch (IOException e) {
    227. e.printStackTrace();
    228. }
    229. return data;
    230. }
    231. /**
    232. * 获取本地图片的字节数
    233. *
    234. * @param imgPath
    235. * @return
    236. */
    237. public static String pathSize(String imgPath) {
    238. File file = new File(imgPath);
    239. FileInputStream fis;
    240. int fileLen = 0;
    241. try {
    242. fis = new FileInputStream(file);
    243. fileLen = fis.available();
    244. } catch (FileNotFoundException e) {
    245. e.printStackTrace();
    246. } catch (IOException e) {
    247. e.printStackTrace();
    248. }
    249. return bytes2kb(fileLen);
    250. }
    251. /**
    252. * 将获取到的字节数转换为KB,MB模式
    253. *
    254. * @param bytes
    255. * @return
    256. */
    257. public static String bytes2kb(long bytes) {
    258. BigDecimal filesize = new BigDecimal(bytes);
    259. // BigDecimal megabyte = new BigDecimal(1024 * 1024);
    260. // float returnValue = filesize.divide(megabyte, 2, BigDecimal.ROUND_UP).floatValue();
    261. // if (returnValue > 1)
    262. // return (returnValue + "MB");
    263. // BigDecimal kilobyte = new BigDecimal(1024);
    264. // returnValue = filesize.divide(kilobyte, 2, BigDecimal.ROUND_UP).floatValue();
    265. // return (returnValue + "KB");
    266. return filesize.toString();
    267. }
    268. public static String getImageFormat(String imagePath) throws IOException {
    269. // 字节数组
    270. byte[] data = null;
    271. String format = null;
    272. // 输入流
    273. InputStream is = null;
    274. // Http连接对象
    275. HttpURLConnection conn = null;
    276. ImageInputStream imageInputStream = null;
    277. try {
    278. // Url对象
    279. URL url = new URL(imagePath);
    280. // 打开连接
    281. conn = (HttpURLConnection) url.openConnection();
    282. // 打开读取 写入是setDoOutput
    283. // conn.setDoOutput(true);
    284. conn.setDoInput(true);
    285. // 设置请求方式
    286. conn.setRequestMethod("GET");
    287. // 设置超时时间
    288. conn.setConnectTimeout(6000);
    289. // 得到访问的数据流
    290. is = conn.getInputStream();
    291. // 验证访问状态是否是200 正常
    292. if (conn.getResponseCode() == 200) {
    293. imageInputStream = ImageIO.createImageInputStream(is);
    294. ImageReader imageReader = ImageIO.getImageReaders(imageInputStream).next();
    295. format = imageReader.getFormatName();
    296. } else {
    297. format = null;
    298. }
    299. } catch (MalformedURLException e) {
    300. e.printStackTrace();
    301. } catch (IOException e) {
    302. e.printStackTrace();
    303. } finally {
    304. try {
    305. if (is != null) {
    306. // 关闭流
    307. is.close();
    308. }
    309. if(imageInputStream != null){
    310. imageInputStream.close();
    311. }
    312. } catch (IOException e) {
    313. e.printStackTrace();
    314. }
    315. // 关闭连接
    316. conn.disconnect();
    317. }
    318. return format;
    319. }
    320. /**
    321. * 将inputStream转化为file
    322. * @param is
    323. * @param file 要输出的文件目录
    324. */
    325. public static void inputStream2File(InputStream is, File file) throws IOException {
    326. OutputStream os = null;
    327. try {
    328. os = new FileOutputStream(file);
    329. int len = 0;
    330. byte[] buffer = new byte[8192];
    331. while ((len = is.read(buffer)) != -1) {
    332. os.write(buffer, 0, len);
    333. }
    334. } finally {
    335. os.close();
    336. is.close();
    337. }
    338. }
    339. /**
    340. * 获取时长
    341. */
    342. public static long getDuration(String filePath) throws Exception {
    343. FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath);
    344. ff.start();
    345. long duration = ff.getLengthInTime() / (1000 * 1000);
    346. ff.stop();
    347. return duration;
    348. }
    349. /**
    350. * 获取视频详情
    351. * @param file
    352. * @return
    353. */
    354. public static VideoInfoVO getVideoInfo(String file) {
    355. VideoInfoVO videoInfoVO = new VideoInfoVO();
    356. FFmpegFrameGrabber grabber = null;
    357. try {
    358. grabber = FFmpegFrameGrabber.createDefault(file);
    359. // 启动 FFmpeg
    360. grabber.start();
    361. // 读取视频帧数
    362. videoInfoVO.setLengthInFrames(grabber.getLengthInVideoFrames());
    363. // 读取视频帧率
    364. videoInfoVO.setFrameRate(grabber.getVideoFrameRate());
    365. // 读取视频秒数
    366. videoInfoVO.setDuration(grabber.getLengthInTime() / 1000000.00);
    367. // 读取视频宽度
    368. videoInfoVO.setWidth(grabber.getImageWidth());
    369. // 读取视频高度
    370. videoInfoVO.setHeight(grabber.getImageHeight());
    371. videoInfoVO.setAudioChannel(grabber.getAudioChannels());
    372. videoInfoVO.setVideoCode(grabber.getVideoCodecName());
    373. videoInfoVO.setAudioCode(grabber.getAudioCodecName());
    374. // String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(file));
    375. videoInfoVO.setSampleRate(grabber.getSampleRate());
    376. return videoInfoVO;
    377. } catch (Exception e) {
    378. e.printStackTrace();
    379. return null;
    380. } finally {
    381. try {
    382. if (grabber != null) {
    383. // 此处代码非常重要,如果没有,可能造成 FFmpeg 无法关闭
    384. grabber.stop();
    385. grabber.release();
    386. }
    387. } catch (FFmpegFrameGrabber.Exception e) {
    388. log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage());
    389. }
    390. }
    391. }
    392. }
    VideoInfoVO实体
    1. @Getter
    2. @Setter
    3. public class VideoInfoVO {
    4. /**
    5. * 总帧数
    6. **/
    7. private int lengthInFrames;
    8. /**
    9. * 帧率
    10. **/
    11. private double frameRate;
    12. /**
    13. * 时长
    14. **/
    15. private double duration;
    16. /**
    17. * 视频编码
    18. */
    19. private String videoCode;
    20. /**
    21. * 音频编码
    22. */
    23. private String audioCode;
    24. private int width;
    25. private int height;
    26. private int audioChannel;
    27. private String md5;
    28. /**
    29. * 音频采样率
    30. */
    31. private Integer sampleRate;
    32. private Double fileSize;
    33. public String toJson() {
    34. try {
    35. ObjectMapper objectMapper = new ObjectMapper();
    36. return objectMapper.writeValueAsString(this);
    37. } catch (Exception e) {
    38. return "";
    39. }
    40. }
    41. }

    InputStream转FileInputStream
    1. /**
    2. * inputStream转FileInputStream
    3. * @param inputStream
    4. * @return
    5. * @throws IOException
    6. */
    7. public static FileInputStream convertToFileInputStream(InputStream inputStream) throws IOException {
    8. File tempFile = File.createTempFile("temp", ".tmp");
    9. tempFile.deleteOnExit();
    10. try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
    11. byte[] buffer = new byte[1024];
    12. int bytesRead;
    13. while ((bytesRead = inputStream.read(buffer)) != -1) {
    14. outputStream.write(buffer, 0, bytesRead);
    15. }
    16. }
    17. return new FileInputStream(tempFile);
    18. }
    获取文件大小
    1. /**
    2. * 获取文件大小
    3. * @param inputStream
    4. * @return
    5. */
    6. public String getFileSize(InputStream inputStream){
    7. FileChannel fc = null;
    8. String size = "0";
    9. try {
    10. FileInputStream fis = convertToFileInputStream(inputStream);
    11. fc = fis.getChannel();
    12. BigDecimal fileSize = new BigDecimal(fc.size());
    13. size = String.valueOf(fileSize);
    14. } catch (FileNotFoundException e) {
    15. e.printStackTrace();
    16. } catch (IOException e) {
    17. e.printStackTrace();
    18. } finally {
    19. if (null != fc) {
    20. try {
    21. fc.close();
    22. } catch (IOException e) {
    23. e.printStackTrace();
    24. }
    25. }
    26. }
    27. return size;
    28. }
    截取视频帧封面
    1. /**
    2. * 截取视频获得指定帧的图片
    3. *
    4. * @param video 源视频文件
    5. * @param picPath 截图存放路径
    6. */
    7. public String getVideoPic(InputStream video, String picPath) {
    8. FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
    9. try {
    10. ff.start();
    11. // 截取中间帧图片(具体依实际情况而定)
    12. int i = 0;
    13. int length = ff.getLengthInFrames();
    14. // int middleFrame = length / 2;
    15. int middleFrame = 5;
    16. Frame frame = null;
    17. while (i < length) {
    18. frame = ff.grabFrame();
    19. if ((i > middleFrame) && (frame.image != null)) {
    20. break;
    21. }
    22. i++;
    23. }
    24. // 截取的帧图片
    25. Java2DFrameConverter converter = new Java2DFrameConverter();
    26. BufferedImage srcImage = converter.getBufferedImage(frame);
    27. int srcImageWidth = srcImage.getWidth();
    28. int srcImageHeight = srcImage.getHeight();
    29. // 对截图进行等比例缩放(缩略图)
    30. int width = 480;
    31. int height = (int) (((double) width / srcImageWidth) * srcImageHeight);
    32. BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    33. thumbnailImage.getGraphics().drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
    34. InputStream inputStream = bufferedImageToInputStream(thumbnailImage);
    35. // File picFile = new File(picPath);
    36. // ImageIO.write(thumbnailImage, "jpg", picFile);
    37. // int available = inputStream.available();
    38. String imgUrl = ossUtils.putThumbInputStream(inputStream, picPath);
    39. ff.stop();
    40. return imgUrl;
    41. } catch (java.lang.Exception e) {
    42. e.printStackTrace();
    43. System.out.println(e);
    44. return null;
    45. }
    46. }

  • 相关阅读:
    地平线 旭日X3 PI (一)首次开机细节
    2000-2020年全国各省财政收支分类明细数据
    【AI视野·今日NLP 自然语言处理论文速览 第七十九期】Thu, 18 Jan 2024
    bitset位集学习
    IDEA操作Sharding-JDBC实战1
    网络编程 10.26
    Git和SVN有什么区别?
    【Java每日一题】— —第十八题:求二维数组中的元素最小值及其索引。(2023.10.02)
    使用Code Chart绘制流程图
    Hierarchy-Aware Global Model for Hierarchical Text Classification
  • 原文地址:https://blog.csdn.net/Just_do_it_HZF/article/details/134293859