Перехват и обработка декодированных кадров при помощи OpenCV¶
Overview¶
Механизм перехвата и обработки декодированных кадров позволяет интегрировать мощную библиотеку обработки изображений OpenCV. Эта возможность может быть полезна, например, при реализации дополненной реальности на стороне сервера, и в других подобных случаях, и доступна начиная со сборки WCS 5.2.1914.
Для того, чтобы реализовать собственный класс для интеграции OpenCV, прежде всего, необходимо собрать саму библиотеку OpenCV.
Building OpenCV¶
Рассмотрим сборку OpenCV 4.9.0 на Centos 7, чтобы обеспечить совместимость с glibc 2.17.
-
Устанавливаем JDK 8 и ANT
-
Определяем местоположение каталога установки JDK и настраиваем переменную
JAVA_HOME
-
Устанавливаем CMake 3.6.2 или новее
-
Устанавливаем GCC 11 и переключаемся на него для сборки OpenCV
-
Скачиваем и распаковываем исходные тексты OpenCV
-
Настраиваем сборку
-
Собираем OpenCV
-
Копируем собранные модули в каталог установки WCS
Реализация перехватчика с использованием OpenCV¶
Для перехвата декодированных кадров необходимо разработать класс на языке Java, реализующий интерфейс IDecodedFrameInterceptor. Функция этого класса frameDecoded() будет получать декодированные кадры в формате YUV I420, создавать OpenCV матрицы для каждого из массивов данных YUV420 и применять размытие
Реализация для транскодинга предыдущего поколения¶
TestInterceptor.java
// Package name should be strictly defined as com.flashphoner.frameInterceptor
package com.flashphoner.frameInterceptor;
import com.flashphoner.sdk.media.IDecodedFrameInterceptor;
import com.flashphoner.sdk.media.YUVFrame;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import java.nio.ByteBuffer;
public class OpenCvInterceptor implements IDecodedFrameInterceptor {
// Load OpenCv java wrapper
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
@Override
public void frameDecoded(String streamName, YUVFrame frame) {
ByteBuffer[] data = frame.getData();
// WCS handles YUV pointers separately. Create Matrix for each YUV420 plane
Mat yMatrix = new Mat(frame.getHeight(), frame.getStrideY(), CvType.CV_8UC1, data[YUVFrame.Y_POINTER_IDX]);
Mat uMatrix = new Mat(frame.getChromaHeight(), frame.getStrideChroma(), CvType.CV_8UC1, data[YUVFrame.U_POINTER_IDX]);
Mat vMatrix = new Mat(frame.getChromaHeight(), frame.getStrideChroma(), CvType.CV_8UC1, data[YUVFrame.V_POINTER_IDX]);
Size gaussianKernelSize = new Size(33, 33);
// Apply Gaussian Blue. Passed kernel size should be odd
Imgproc.GaussianBlur(yMatrix, yMatrix, gaussianKernelSize, 0);
Imgproc.GaussianBlur(uMatrix, uMatrix, gaussianKernelSize, 0);
Imgproc.GaussianBlur(vMatrix, vMatrix, gaussianKernelSize, 0);
}
}
Реализация для транскодинга 3 поколения¶
TestInterceptor.java
// Package name should be strictly defined as com.flashphoner.frameInterceptor
package com.flashphoner.frameInterceptor;
import com.flashphoner.sdk.media.IDecodedFrameInterceptor;
import com.flashphoner.sdk.media.YUVFrame;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import java.nio.ByteBuffer;
public class OpenCvInterceptor implements IDecodedFrameInterceptor {
// Load OpenCv java wrapper
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
@Override
public void frameDecoded(String streamName, YUVFrame frame) {
if (!frame.isNewGen()) {
// skip only GEN3 supported
return;
}
ByteBuffer[] data = frame.getData();
// WCS handles YUV pointers separately. Create Matrix for each YUV420 plane
Mat yMatrix = new Mat(frame.getHeight(), frame.getStrideY(), CvType.CV_8UC1, data[YUVFrame.Y_POINTER_IDX]);
Mat uMatrix = new Mat(frame.getChromaHeight(), frame.getStrideChroma(), CvType.CV_8UC1, data[YUVFrame.U_POINTER_IDX]);
Mat vMatrix = new Mat(frame.getChromaHeight(), frame.getStrideChroma(), CvType.CV_8UC1, data[YUVFrame.V_POINTER_IDX]);
Size gaussianKernelSize = new Size(33, 33);
// Apply Gaussian Blue. Passed kernel size should be odd
Imgproc.GaussianBlur(yMatrix, yMatrix, gaussianKernelSize, 0);
Imgproc.GaussianBlur(uMatrix, uMatrix, gaussianKernelSize, 0);
Imgproc.GaussianBlur(vMatrix, vMatrix, gaussianKernelSize, 0);
}
}
Сборка и использование примера¶
Затем следует скомпилировать класс в байт-код. Для этого создаем дерево каталогов, соответствующее названию пакета написанного класса
и выполняем команду
javac -cp /usr/local/FlashphonerWebCallServer/lib/wcs-core.jar:/usr/local/FlashphonerWebCallServer/lib/custom/opencv-490.jar ./com/flashphoner/frameInterceptor/TestInterceptor.java
Теперь упакуем скомпилированный код в jar-файл
и скопируем его в каталог, где размещены собственные Java библиотеки для интеграции с WCS
Для того, чтобы использовать разработанный класс, необходимо указать имя его пакета в настройке в файле flashphoner.properties
и перезапустить WCS.
Тестирование¶
-
Опубликуйте поток в примере Two Way Streaming

-
Проиграйте поток в примере Player с указанием разрешения, чтобы включился транскодинг, например
https://test1.flashphoner.com:8444/client2/examples/demo/streaming/player/player.html?resolution=320x240,
гдеtest1.flashphoner.comадрес WCS сервера

Изображение будет размыто.