Описание
При включенном транскодинге существует возможность перехватывать декодированные кадры опубликованного потока в формате YUV и изменять их содержимое попиксельно на стороне сервера. Кадр, в который были внесены изменения, будет закодирован и отправлен в выходной поток транскодера.
Реализация перехватчика
Для перехвата декодированных кадров необходимо разработать класс на языке Java, реализующий интерфейс IDecodedFrameInterceptor. Функция этого класса frameDecoded() будет получать декодированные кадры в формате YUV, например
// 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; /** * Custom decoded frames interceptor implementation example * The example draws a cross over the picture */ public class TestInterceptor implements IDecodedFrameInterceptor { // Constants to parse pixel private final int Y = 0; private final int U = 1; private final int V = 2; // Dark colored pixel private final byte[] DarkPixel = new byte []{42, -128, -128}; /** * Function to handle decoded frame * @param streamName - stream name * @param frame - decoded YUV frame */ @Override public void frameDecoded(String streamName, YUVFrame frame) { // Get frame height int frameHeight = frame.getHeight(); // Get frame width int frameWidth = frame.getWidth(); // Declare cross lines padding int PADDING = 4; // Define frame center int frameCenterX = frameWidth / 2; int frameCenterY = frameHeight / 2; // Define vertical line bounds int leftBound = frameCenterX - PADDING; int rightBound = frameCenterX + PADDING; // Define horizontal line bounds int topBound = frameCenterY - PADDING; int bottomBound = frameCenterY + PADDING; // Walk through the frame pixels and draw a cross for (int x = 0; x < frameWidth; x++) { for (int y = 0; y < frameHeight; y++) { if (validateCoord(x, leftBound, rightBound) || validateCoord(y, topBound, bottomBound)) { // Read the pixel byte[] pixel = frame.readPixel(x, y); // Modify the pixel pixel[Y] = DarkPixel[Y]; pixel[U] = DarkPixel[U]; pixel[V] = DarkPixel[V]; // Write the pixel back frame.writePixel(x, y, pixel); } } } } /** * Helper function to validate pixel drawing * @param coord - pixel coordinate * @param low - low coordinate bound * @param high - high coordinate bound * @return true if coordinate is valid */ private boolean validateCoord(int coord, int low, int high) { return (coord > low && coord < high); } }
Затем следует скомпилировать класс в байт-код. Для этого создаем дерево каталогов, соответствующее названию пакета написанного класса
mkdir -p com/flashphoner/frameInterceptor
и выполняем команду
javac -cp /usr/local/FlashphonerWebCallServer/lib/wcs-core.jar ./com/flashphoner/frameInterceptor/TestInterceptor.java
Теперь упакуем скомпилированный код в jar-файл
jar -cf testlayout.jar ./com/flashphoner/frameInterceptor/TestInterceptor.class
и скопируем его в каталог, где размещены библиотеки WCS сервера
cp testinterceptor.jar /usr/local/FlashphonerWebCallServer/lib
Для того, чтобы использовать разработанный класс, необходимо указать имя его пакета в настройке в файле flashphoner.properties
decoded_frame_interceptor=com.flashphoner.frameInterceptor.TestInterceptor
и перезапустить WCS.
Отдельный каталог для собственных Java библиотек
Начиная со сборки 5.2.1512, Java библиотеки (jar файлы) должны помещаться в каталог /usr/local/FlashphonerWebCallServer/lib/custom
cp testlayout.jar /usr/local/FlashphonerWebCallServer/lib/custom
Этот каталог сохраняется при дальнейших обновлениях сервера к более новым сборкам. Таким образом, нет необходимости снова копировать jar файлы после установки обновления.
Тестирование
1. Опубликуйте поток в примере Two Way Streaming https://test1.flashphoner.com:8444/client2/examples/demo/streaming/two_way_streaming/two_way_streaming.html, где test1.flashphoner.com - адрес WCS сервера
2. Проиграйте поток в примере Player с указанием разрешения, чтобы включился транскодинг, например https://test1.flashphoner.com:8444/client2/examples/demo/streaming/player/player.html?resolution=320x240, где test1.flashphoner.com - адрес WCS сервера
На изображении будут видны измененные пиксели.
Управление перехватчиками по REST API
В сборке 5.2.2055 добавлена возможность управления перехватчиками декодированных кадров по REST API
REST-запрос должен быть HTTP/HTTPS POST запросом в таком виде:
- HTTP: http://streaming.flashphoner.com:8081/rest-api/video_interceptor/set
- HTTPS: https://streaming.flashphoner.com:8444/rest-api/video_interceptor/set
Здесь:
- streaming.flashphoner.com - адрес WCS-сервера
- 8081 - стандартный REST / HTTP порт WCS-сервера
- 8444 - стандартный HTTPS порт
- rest-api - обязательный префикс
- /video_interceptor/set - используемый REST-вызов
REST методы и статусы ответа
/video_interceptor/set
Назначить потоку обработчик декодированных фреймов. Обработчик начнет работать, как только поток начнет декодироваться, например, будет добавлен в микшер. На один поток может быть назначен один обработчик
Request example
POST /rest-api/video_interceptor/set HTTP/1.1 Host: localhost:8081 Content-Type: application/json { "streamName":"stream1", "className":"com.flashphoner.frameInterceptor.TestInterceptor" }
Response example
HTTP/1.1 200 OK Access-Control-Allow-Origin: * Content-Type: application/json
Return codes
Code | Reason |
---|---|
200 | OK |
404 | Not found |
500 | Internal server error |
/video_interceptor/find_all
Найти все назначенные обработчики
Request example
POST /rest-api/video_interceptor/find_all HTTP/1.1 Host: localhost:8081 Content-Type: application/json { "streamName":"stream1", "className":"com.flashphoner.frameInterceptor.TestInterceptor" }
Response example
HTTP/1.1 200 OK Access-Control-Allow-Origin: * Content-Type: application/json [ { "hashcode":21982654, "className":"com.flashphoner.frameInterceptor.TestInterceptor", "processedFrames":169, "streamName":"stream1", "status":"PROCESSING" } ]
Return codes
Code | Reason |
---|---|
200 | OK |
404 | Not found |
/video_interceptor/remove
Удалить обработчик с указанного потока
Request example
POST /rest-api/video_interceptor/remove HTTP/1.1 Host: localhost:8081 Content-Type: application/json { "streamName":"stream1" }
Response example
HTTP/1.1 200 OK Access-Control-Allow-Origin: * Content-Type: application/json
Return codes
Code | Reason |
---|---|
200 | OK |
404 | Not found |
Параметры
Параметр | Описание | Пример |
---|---|---|
streamName | Имя опубликованного потока | "streamName":"stream1" |
className | Имя класса перехватчика | "className":"com.flashphoner.frameInterceptor.TestInterceptor" |
hashcode | Идентификатор объекта перехватчика | "hashcode":21982654 |
status | Текущий статус перехватчика | "status":"PROCESSING" |
processedFrames | Количество обработанных кадров | "processedFrames":169 |
Перехватчик может находиться в одном из следующих состояний:
- WAITING - перехватчик ожидает, когда поток начнет декодироваться
- PROCESSING - перехватчик получает декодированные кадры и обрабатывает их
- UNSUPPORTED - перехват не поддерживается (например, для потоков, декодированных на GPU)