JAVA CV 開發
我這個項目最終還是要要用在安卓的機器上的,但是安卓那邊說推流推不了,我只好自己先試試電腦的本地攝像頭,后面再准備把這個打包給安卓,這個是我本人在學習Java CV 所做的一個總結
參考:
OpenCV示例
常見錯誤:
org.bytedeco.javacv.FrameRecorder$Exception: avio_open2 error() error -138: Could not open 'null'
需要使用 nginx服務 代理推送流 這個具體去看 rtsp轉rtmp 那篇 博客


關閉攝像頭錄像報錯
org.bytedeco.javacv.FrameGrabber$Exception: videoInput.getPixels() Error: Could not get pixels.


解決:
javacv-platform 版本升級為 1.5.4 能解決大部分問題,只需要引這一個依賴
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.4</version> </dependency>
多線程控制
類對象在內存唯一
獲取配置文 .properties 件內容
推流器
代碼內容 推流和加水印
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);//如果有多個攝像頭請改這個。0表示第一個 //360p 480x360 或 480p 640x480 grabber.setImageWidth(480); grabber.setImageHeight(360); grabber.start(); //開始獲取攝像頭數據 CanvasFrame frame = new CanvasFrame("camera");//新建一個窗口 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setAlwaysOnTop(true); OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();//轉換器 IplImage grabbedImage = converter.convert(grabber.grab());//抓取一幀視頻並將其轉換為圖像,至於用這個圖像用來做什么?加水印,人臉識別等等自行添加 // 水印文字位置 Point point = new Point(50, 30); // 顏色,使用黃色 Scalar scalar = new Scalar(0, 255, 255, 0); int width = grabbedImage.width(); int height = grabbedImage.height(); FrameRecorder recorder = FrameRecorder.createDefault(outputFile, width, height); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // avcodec.AV_CODEC_ID_H264,編碼 recorder.setFormat("flv");//封裝格式,如果是推送到rtmp就必須是flv封裝格式 recorder.setFrameRate(frameRate); recorder.start();//開啟錄制器 long startTime = 0; long videoTS = 0; Mat mat = null; Frame rotatedFrame = converter.convert(grabbedImage);//不知道為什么這里不做轉換就不能推到rtmp while ((grabbedImage = converter.convert(grabber.grab())) != null) { String now = sdf.format(System.currentTimeMillis()); rotatedFrame = converter.convert(grabbedImage); // 取一幀視頻(圖像),並轉換為Mat mat = converter.convertToMat(rotatedFrame); opencv_imgproc.putText(mat, now, point, opencv_imgproc.INTER_LINEAR, 1.2, scalar); frame.showImage(converter.convert(mat)); if (startTime == 0) { startTime = System.currentTimeMillis(); } videoTS = 1000 * (System.currentTimeMillis() - startTime); recorder.setTimestamp(videoTS); recorder.record(rotatedFrame); //和幀率相關,不設置則是全幀率 Thread.sleep(40); } mat.release(); mat.close(); frame.dispose();//關閉窗口 recorder.close();//關閉推流錄制器,close包含release和stop操作 grabber.close();//關閉抓取器
本地攝像頭加水印
添加水印出錯
java.lang.NullPointerException: Pointer address of argument 0 is NULL.
解決:
mat不能在循環中關閉或釋放
關鍵代碼
// 水印文字位置 Point point = new Point(30, 30); // 顏色,使用黃色 Scalar scalar = new Scalar(0, 255, 255, 0); ... mat = converter.convertToMat(rotatedFrame); // 加文字水印,opencv_imgproc.putText(圖片,水印文字,文字位置,字體,字體大小,字體顏色 opencv_imgproc.putText(mat,"111",point,1,1.2,scalar); frame.showImage(converter.convert(mat));
去水印參考:
收流器
rtmp流測試
湖南TV rtmp://58.200.131.2:1935/livetv/hunantv
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile); //分辨率 grabber.setImageWidth(480); grabber.setImageHeight(360); // 流媒體輸出地址,分辨率(長,高),是否錄制音頻(0:不錄制/1:錄制) FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 480, 360, audioChannel); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFormat("flv"); //建議在線程中使用該方法 try { grabber.start(); recorder.start(); Frame frame = null; while (status&& (frame = grabber.grabFrame()) != null) { recorder.record(frame); Thread.sleep(40); System.out.println(sdf.format(System.currentTimeMillis())); } recorder.stop(); grabber.stop(); } finally { if (grabber != null) { grabber.stop(); } } 問題: 1、 發現保存下的視頻播放非常快,兩倍速,分析發現視頻幀將近一半丟失 https://www.cnblogs.com/svenwu/p/9663038.html 其他因素: 收流線程不能像發流那樣設置線程睡眠 grabber.setOption("http_transport","tcp"); grabber.setFrameRate(25); ... recorder.setFrameRate(25); //一般考慮比推流器發送的視頻比特流大,如下圖 recorder.setVideoBitrate(1000 * 1000);
問題:
1、 發現保存下的視頻播放非常快,接近兩倍速,分析發現視頻幀將近一半丟失
解決:
其他因素: 收流線程不能像發送流那樣設置線程睡眠
收流器的 grabber 和 recorder 都要設置幀率
grabber.setOption("http_transport","tcp"); grabber.setFrameRate(25); ... recorder.setFrameRate(25); //一般考慮比推流器發送的視頻比特流大,如下圖 recorder.setVideoBitrate(1000 * 1000);

推流視頻流信息
2、等待推流器關閉再等待收流器自動關閉,會有一段等待收流的畫面卡住暫停時間,這部分也被算入到視頻時長,視頻時長不正確
手動關閉收流和推流在VLC中比較


手動關閉收流器會導致視頻時長信息丟失
不同分辨率視頻大小參考
144p (192×144,20幀/秒),4:3,錄制一分鍾大約1MB;
240p (320×240,20幀/秒),4:3,錄制一分鍾大約3MB;
360p (480×360,20幀/秒) ,4:3,錄制一分鍾大約7MB;
480p (640×480,20幀/秒),4:3,錄制一分鍾大約12MB;
720p (1280×720,30幀/秒) , 16:9,錄制一分鍾大約35MB;
1080p (1920×1080,30幀/秒) ,16:9 , 錄制一分鍾大約80MB。