獲取視頻處理對象的方式有不少,讀取本地文件、讀取url、讀取攝像頭等,而直接讀流解析視頻的實施方案卻難以尋覓。此處有兩種方案處理視頻流(此處設定場景爲用戶上傳視頻,同時兩種方式均需服務端安裝ffmpeg+opencv):java
該方案沒有太多技術含量,直接藉助java.io+opencv-VideoCapture便可實現視頻的解幀等操做。linux
本地保存爲求方便,直接使用 apache.commons.io.FileUtils.copyInputStreamToFile(InputStream,File)方法apache
// MultipartFile videoFile InputStream videoInputStream = videoFile.getInputStream(); File file = new File(path + getRandomFileName() + ".mp4"); FileUtils.copyInputStreamToFile(videoInputStream,file);
此處視頻解析,能夠直接使用整合了ffmpeg的opencv中的VideoCapture對象來操做dom
VideoCapture = new VideoCapture(file.getPath());
項目業務要求,取視頻前兩秒的20幀,轉儲爲Mat矩陣的集合ide
// 此處的視頻操做常量來自 javacv Double rawFps = videoCapture.get(opencv_highgui.CV_CAP_PROP_FPS);// 幀率 Double validFps = Math.min(10.0,rawFps);// 校驗 Double validTimeGap = 1.0 / validFps; List<Mat> frameList = new ArrayList(); try { Double currentTime = 0.0; while (currentTime + EPSILON < timeCount) {//EPSILON爲浮點數操做修正值 // 設置視頻的位置 videoCapture.set(opencv_highgui.CV_CAP_PROP_POS_MSEC,currentTime * 1000); Mat frame = new Mat(); capture.read(frame); frameList.add(frame); currentTime += validTimeGap; } } catch .... finally ..
直接讀流的依賴支撐來自 Bytedeco - javacv - FFmpegFrameGrabber 類,在此 向Bytedeco團隊致敬。ui
InputStream inputStream = videoFile.getInputStream(); FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream);
FFmpegFrameGrabber與VideoCapture在開閉時有所不一樣,VideoCapture若是直接構造來初始化不需手動open()即打開,FFmpegFrameGrabber有一專屬方法來打開視頻解析 - start() 。url
grabber.start(); // get each mat List<Mat> mats = new ArrayList<>(); double fps = grabber.getFrameRate(); double each = Math.ceil(fps / fpsDefine); double count = fps * timeCount ; for (int i = 0 ; i < count ; i++) { double mod = i % each; Frame frame = grabber.grabImage(); if (mod == 0.0) { OpenCVFrameConverter.ToMat toMat = new OpenCVFrameConverter.ToMat(); opencv_core.Mat mat = toMat.convert(frame); if (mat != null) { Mat matUse = new Mat(mat.clone().address()); mats.add(matUse); mat.release(); } } }
1.bytedeco - ffmpeg 包中整合有Frame - Mat - BufferImage的相關轉換方法,實際應用中需注意其與opencv - Mat的轉換spa
2.兩者都依賴ffmpeg+opencv本地方法,而pom依賴又有不一樣:code
VideoCapture:視頻
<dependency> <groupId>org.opencv</groupId> <artifactId>opencv</artifactId> <version>2.4.13</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.4.3</version> </dependency>
FFmpegFrameGrabber:
<dependency> <groupId>org.opencv</groupId> <artifactId>opencv</artifactId> <version>2.4.13</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>org.bytedeco.javacpp-presets</groupId> <artifactId>opencv</artifactId> <version>3.4.3-1.4.3</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco.javacpp-presets</groupId> <artifactId>ffmpeg</artifactId> <version>4.0.2-1.4.3</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <version>1.4.3</version> </dependency>
3.都需手動對本地資源加以釋放:這裏包括io流,視頻流,Mat矩陣,同時釋放的方法又有不一樣
VideoCapture:release()
FFmpegFrameGrabber : stop()
finally { try { inputStream.close(); } catch (IOException e) { log.error("close InputStream error : " , e); } try { grabber.stop(); } catch (FrameGrabber.Exception e) { log.error("stop grabber error : " , e); } for (Mat mat : mats) { if (mat != null) { mat.release(); } } }