Java之視頻讀取IO流解幀實施方案


  獲取視頻處理對象的方式有很多,讀取本地文件、讀取url、讀取攝像頭等,而直接讀流解析視頻的實施方案卻難以尋覓。此處有兩種方案處理視頻流(此處設定場景為用戶上傳視頻,同時兩種方式均需服務端安裝ffmpeg+opencv):

  1.io流保存本地再讀取

      該方案沒有太多技術含量,直接借助java.io+opencv-VideoCapture即可實現視頻的解幀等操作。

      1)保存本地

        本地保存為求方便,直接使用 apache.commons.io.FileUtils.copyInputStreamToFile(InputStream,File)方法

// MultipartFile videoFile
InputStream videoInputStream = videoFile.getInputStream();
File file = new File(path + getRandomFileName() + ".mp4");
FileUtils.copyInputStreamToFile(videoInputStream,file);

      2)  視頻解析

        此處視頻解析,可以直接使用整合了ffmpeg的opencv中的VideoCapture對象來操作

VideoCapture = new VideoCapture(file.getPath());

      3) 業務要求

        項目業務要求,取視頻前兩秒的20幀,轉儲為Mat矩陣的集合

// 此處的視頻操作常量來自 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 ..

  2.直接讀流

    直接讀流的依賴支撐來自 Bytedeco - javacv - FFmpegFrameGrabber 類,在此 向Bytedeco團隊致敬

    1)讀io,轉FFmpegFrameGrabber

InputStream inputStream = videoFile.getInputStream();
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream);

    2)業務要求

    FFmpegFrameGrabber與VideoCapture在開閉時有所不同,VideoCapture如果直接構造來初始化不需手動open()即打開,FFmpegFrameGrabber有一專屬方法來打開視頻解析 - start() 。

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();
        }
    }
}

  3.兩種方式的異同

    1.bytedeco - ffmpeg 包中整合有Frame - Mat - BufferImage的相關轉換方法,實際應用中需注意其與opencv - Mat的轉換

    2.二者都依賴ffmpeg+opencv本地方法,而pom依賴又有不同:

    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();
                }
            }
        }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM