【案發現場】JavaCV 下 CPU 飆升到 260%

描述

Spring Boot 引入 JavaCV 作視頻圖像處理,固然首選FFmpeg了,內心碎碎念,開源的東西就是好用。因而啪啦啪啦編碼一成天,實現了RTMP流媒體推送每間隔時間截取幀圖片流媒體轉MP4保存後用於回放三個主要功能。html

打完Jar包後一看,560M大小,天呢!!! 一會 exclusions一下。java

碎碎念!!!bash

運行了一下,兩路RTSP流,很輕鬆!CPU佔用不到10%,內存1.1G,能夠接受。服務器

因而,果斷髮往服務器,而後啓動……兩路RTSP流,CPU果斷飄升到260%……完蛋!!!異步

因而開始調優,視頻解碼真心消耗資源,無奈中……ide

JVM尋找問題&調優

網上各種這種文章,這裏很少作介紹,通常就是兩步,首先尋找CPU高的線程;而後找到線程的記錄,看看有啥問題。學習

發現了問題

JavaCV 會對流媒體進行解析,這就很耗資源,無解的難題。測試

因而,最好的解決方案就是,不解析視頻。 FFmpeg支持 codec copy,而 JavaCV也能夠經過 AVPacket實現。ui

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.bytedeco.javacv.*;

import org.bytedeco.ffmpeg.avcodec.AVPacket;

public class PacketRecorderTest {

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd__hhmmSSS");

    private static final int RECORD_LENGTH = 5000;

    private static final boolean AUDIO_ENABLED = false;

    public static void main(String[] args) throws FrameRecorder.Exception, FrameGrabber.Exception {

        String inputFile = "/home/usr/videos/VIDEO_FILE_NAME.mp4";

        // Decodes-encodes
        String outputFile = "/tmp/" + DATE_FORMAT.format(new Date()) + "_frameRecord.mp4";
        PacketRecorderTest.frameRecord(inputFile, outputFile);

        // copies codec (no need to re-encode)
        outputFile = "/tmp/" + DATE_FORMAT.format(new Date()) + "_packetRecord.mp4";
        PacketRecorderTest.packetRecord(inputFile, outputFile);

    }

    public static void frameRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception {

        int audioChannel = AUDIO_ENABLED ? 1 : 0;

        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);

        grabber.start();
        recorder.start();

        Frame frame;
        long t1 = System.currentTimeMillis();
        while ((frame = grabber.grabFrame(AUDIO_ENABLED, true, true, false)) != null) {
            recorder.record(frame);
            if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) {
                break;
            }
        }
        recorder.stop();
        grabber.stop();
    }

    public static void packetRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception {

        int audioChannel = AUDIO_ENABLED ? 1 : 0;

        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);

        grabber.start();
        recorder.start(grabber.getFormatContext());

        AVPacket packet;
        long t1 = System.currentTimeMillis();
        while ((packet = grabber.grabPacket()) != null) {
            recorder.recordPacket(packet);
            if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) {
                break;
            }
        }

        recorder.stop();
        grabber.stop();

    }
}
複製代碼

感覺一下,仍是好用的。可是在進行流媒體的轉換時,總會出現各類問題。編碼

最後的解決方案

碎碎念!!!

仍是直接使用 Process 調用 FFmpeg吧!

import org.bytedeco.javacpp.Loader;

import java.io.IOException;

public class CommondTest {

    public static void main(String[] args) throws IOException, InterruptedException {
        String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
        String from = "rtsp://*************";
        String to = "rtmp://********************";
        ProcessBuilder pb = new ProcessBuilder(ffmpeg,
            "-i", from, "-codec", "copy", "-f", "flv", "-y", to);
        Process process = pb.inheritIO().start();
        process.waitFor();
    }
}
複製代碼

測試了一下,仍是好用的,那麼能夠開始改造了!

學會 FFmpeg 命令

學習知識的最好途徑永遠是官網,由於權威 ffmpeg Documentation

在用java調用的時候,要設置好輸出,防止由於命令行的輸出流沒有被讀取而形成堵塞!

因此,仍是把ffmpeg的日誌級別設置一下吧,-loglevel quiet

CPU下來了

終於下來了!

技術問題解決後,剩下的截圖,調用某度雲的故障識別接口並實時報警……

碎碎念!!!某度雲提供了5個圖片故障識別的接口,每張截圖得分別調用5個接口……,看來必然要使用異步了……

服務器信息 (4核8G/2.4GHz)

服務器CPU基本信息: cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

4  Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
複製代碼

服務器內存基本信息: cat /proc/meminfo

MemTotal:        8008500 kB
複製代碼

物理CPU個數: cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

1
複製代碼

核數: cat /proc/cpuinfo| grep "cpu cores"| uniq

cpu cores	: 2
複製代碼

邏輯CPU的個數: cat /proc/cpuinfo| grep "processor"| wc -l

4
複製代碼

六路攝像頭下消耗資源: CPU消耗 20.3%,內存佔用1.3G

top - 18:14:33 up 17 days, 7 min,  1 user,  load average: 1.03, 0.92, 0.80
Tasks: 105 total,   1 running, 104 sleeping,   0 stopped,   0 zombie
%Cpu(s): 20.3 us,  0.6 sy,  0.0 ni, 78.7 id,  0.0 wa,  0.0 hi,  0.3 si,  0.1 st
KiB Mem :  8008500 total,   120588 free,  1940580 used,  5947332 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  5368844 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
28360 root      20   0 6307744   1.3g  24644 S  83.4 16.4  42:04.91 java
複製代碼

查看網卡流量: watch -n 1 "/sbin/ifconfig eth0 | grep bytes"

RX packets 234768965  bytes 315037366631 (293.4 GiB)
        TX packets 224227493  bytes 113722298707 (105.9 GiB)
複製代碼
相關文章
相關標籤/搜索