Skip to end of metadata
Go to start of metadata

Описание

Механизм перехвата и обработки декодированных кадров позволяет интегрировать мощную библиотеку обработки изображений OpenCV. Эта возможность может быть полезна, например, при реализации дополненной реальности на стороне сервера, и в других подобных случаях, и доступна начиная со сборки WCS 5.2.1914.

Для того, чтобы реализовать собственный класс для интеграции OpenCV, прежде всего, необходимо собрать саму библиотеку OpenCV.

Сборка OpenCV

Рассмотрим сборку OpenCV 4.9.0 на Centos 7, чтобы обеспечить совместимость с glibc 2.17.

1. Устанавливаем JDK 8 и ANT

yum install openjdk-8-jdk ant

2. Определяем местоположение каталога установки JDK и настраиваем переменную JAVA_HOME 

export JAVA_HOME=$(echo $(readlink -f $(which javac)) | sed 's/\/bin\/javac//g')

3. Устанавливаем CMake 3.6.2 или новее

wget https://cmake.org/files/v3.6/cmake-3.6.2.tar.gz
tar -zxf cmake-3.6.2.tar.gz 
cd cmake-3.6.2
./bootstrap --prefix=/usr/local
make -j$(nproc)
make install

4. Устанавливаем GCC 11 и переключаемся на него для сборки OpenCV

yum install centos-release-scl
yum install devtoolset-11-gcc devtoolset-11-gcc-c++
scl enable devtoolset-11 -- bash

5. Скачиваем и распаковываем исходные тексты OpenCV

wget -O opencv-490.zip https://github.com/opencv/opencv/archive/4.9.0.zip
unzip opencv-490.zip

6. Настраиваем сборку

cd opencv-490
mkdir build
cd build
cmake ..

7. Собираем OpenCV

make -j $(nproc)

8. Копируем собранные модули в каталог установки WCS

cp bin/opencv-490.jar /usr/local/FlashphonerWebCallServer/lib/custom
cp -r lib/* /usr/local/FlashphonerWebCallServer/lib/so

Реализация перехватчика с использованием OpenCV

Для перехвата декодированных кадров необходимо разработать класс на языке Java, реализующий интерфейс `IDecodedFrameInterceptor`. Функция этого класса frameDecoded() будет получать декодированные кадры в формате YUV I420, конвертировать их средствами OpenCV в формат RGB, применять размытие, конвертировать результат обратно в YUV I420 и перезаписывать данные фрейма не попиксельно, а одним массивом при помощи метода Frame.rewriteData() 

TestInterceptor.java
// Package name should be strictly defined as com.flashphoner.frameInterceptor
package com.flashphoner.frameInterceptor;
 
// Import decoded frame interceptor interface
import com.flashphoner.sdk.media.IDecodedFrameInterceptor;
// Import YUV frame description
import com.flashphoner.sdk.media.YUVFrame;
// Import OpenCV classes
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;

public class TestInterceptor implements IDecodedFrameInterceptor {

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    @Override
    public void frameDecoded(String streamName, YUVFrame frame) {

        int width = frame.getWidth();
        int height = frame.getHeight();
        // Calculate chroma height of YUV frame
        int chromaHeight = height * 3 / 2;

        Mat srcYuvMatrix = new Mat(chromaHeight, width, CvType.CV_8UC1, frame.getData());

        // Create RGB matrix for manipulations
        Mat rgbMatrix = new Mat(height, width, CvType.CV_8UC3);
        Mat blurredMatrix = new Mat(height, width, CvType.CV_8UC3);

        // Convert YUV matrix to RGB matrix
        Imgproc.cvtColor(srcYuvMatrix, rgbMatrix, Imgproc.COLOR_YUV2RGB_I420);
        // Apply blur to matrix
        Imgproc.GaussianBlur(rgbMatrix, blurredMatrix, new Size(15, 15), 0);

        Mat dstYuvMatrix = new Mat(chromaHeight, width, CvType.CV_8UC1);
        // Convert RGB back to YUV
        Imgproc.cvtColor(blurredMatrix, dstYuvMatrix, Imgproc.COLOR_RGB2YUV_I420);
        byte[] dstData = new byte[chromaHeight * width];
        // get data from destination matrix
        dstYuvMatrix.get(0,0, dstData);

        // Current method rewrites full frame data with provided dstData
        // This method is recommended for a complete rewrite of the frame, rather than pixel-by-pixel rewriting due to color mismatch
        frame.rewriteData(dstData);
    }

}

Затем следует скомпилировать класс в байт-код. Для этого создаем дерево каталогов, соответствующее названию пакета написанного класса

mkdir -p com/flashphoner/frameInterceptor

и выполняем команду

javac -cp /usr/local/FlashphonerWebCallServer/lib/wcs-core.jar:/usr/local/FlashphonerWebCallServer/lib/custom/opencv-490.jar ./com/flashphoner/frameInterceptor/TestInterceptor.java

Теперь упакуем скомпилированный код в jar-файл

jar -cf testinterceptor.jar ./com/flashphoner/frameInterceptor/TestInterceptor.class

и скопируем его в каталог, где размещены собственные Java библиотеки для интеграции с WCS

cp testinterceptor.jar /usr/local/FlashphonerWebCallServer/lib/custom

Для того, чтобы использовать разработанный класс, необходимо указать имя его пакета в настройке в файле flashphoner.properties

decoded_frame_interceptor=com.flashphoner.frameInterceptor.TestInterceptor

и перезапустить WCS.

Тестирование

1. Опубликуйте поток в примере Two Way Streaming

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

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