Добавление стримов в микшер

fitsar

New Member
У меня во всех именах стримах присутствует название типов юзеров , я их сортирую и добавляю в определенной последовательности. Но не всегда она размещаются в микшере также
есть какая-то логика размещения стрима в микшере от очередности добавление ?
 

Max

Administrator
Staff member
Добрый день.
Нет, никакой очередности при добавлении в микшер нет. Если необходимо соблюдать такую очередность, придется реализовать собственный Java класс для размещения картинок в микшере. Основной метод этого класс computeLayout получает на вход список имен стримов и массив картинок в формате YUV, соответственно, Вы можете отсортировать список и разместить картинки с необходимой очередностью. Посмотрите также этот пост, там есть пример такого Java класса с размещением картинки определенного потока в зависимости от имени на весь экран как фона для остальных картинок.
 

fitsar

New Member
вот мой код
Java:
// Package name should be strictly defined as com.flashphoner.mixerlayout
package com.flashphoner.mixerlayout;

// Import mixer layout interface
import com.flashphoner.media.mixer.video.presentation.BoxPosition;
import com.flashphoner.sdk.media.IVideoMixerLayout;
// Import YUV frame description
import com.flashphoner.sdk.media.YUVFrame;
// Import Box class for picture operations
import com.flashphoner.media.mixer.video.presentation.Box;
// Import MixerConfig class
import com.flashphoner.server.commons.rmi.data.impl.MixerConfig;

// Import Java packages to use
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;

/**
* Custom mixer layout implementation example
*/
public class FullscreenLayout implements IVideoMixerLayout {

    // Pictures padding, should be even (or zero if no padding needed)
    private static final int PADDING = 0;

    /**
     * Function to compute layout, will be called by mixer before encoding output stream picture
     * This example computes one-line grid layout
     * @param yuvFrames - incoming streams raw pictures array in YUV format
     * @param strings - incoming streams names array
     * @param canvasWidth - mixer output picture canwas width
     * @param canvasHeight - mixer output picture canwas heigth
     * @return array of pictures layouts
     */
    @Override
    public Layout[] computeLayout(YUVFrame[] yuvFrames, String[] strings, int canvasWidth, int canvasHeight) {
        // This object represents mixer canvas
        Box mainBox = new Box(null, canvasWidth, canvasHeight);

        // Find every picture dimensions (this are the same for all the pictures because this is grid layout)
        int frameBoxWidth = canvasWidth / yuvFrames.length;

        // Container to place stream pictures
        Box container = new Box(mainBox, frameBoxWidth, canvasHeight);
        container.setPosition(BoxPosition.BOTTOM);
        if (strings[0].contains("user_aginsts")) {
            List<YUVFrame> FramesList = Arrays.asList(yuvFrames);
            Collections.reverse(FramesList);
            yuvFrames = FramesList.to_Array();
        }

        for (int i = 0; i < yuvFrames.length; i++) {
            // This rectangle can include stream caption if mixer_text_outside_frame setting is enabled
            Box frameBox = new Box(container, frameBoxWidth, canvasHeight);
            frameBox.setPosition(BoxPosition.INLINE_HORIZONTAL_CENTER);
            // Compute picture frame placement including stream caption text
            Box frame = Box.computeBoxWithFrame(frameBox, yuvFrames[i]);
            // Adjust picture to the parent rectangle
            frame.fillParent();
        }
        // Prepare an array to return layout calculated
        ArrayList<IVideoMixerLayout.Layout> layout = new ArrayList<>();
        // Calculate mixer layout
        mainBox.computeLayout(layout);
        // Return the result
        return layout.toArray(new IVideoMixerLayout.Layout[layout.size()]);
    }

    /**
     * The function for internal use. Must be overriden.
     */
    @Override
    public void setConfig(MixerConfig mixerConfig) {
    }
}
когда пытался применить этот файла , делал все как в доке
мне упали ошибки
Code:
com/flashphoner/mixerlayout/FullscreenLayout.java:5: error: cannot find symbol
import com.flashphoner.media.mixer.video.presentation.BoxPosition;
                                                     ^
  symbol:   class BoxPosition
  location: package com.flashphoner.media.mixer.video.presentation
com/flashphoner/mixerlayout/FullscreenLayout.java:10: error: cannot find symbol
import com.flashphoner.media.mixer.video.presentation.Box;
                                                     ^
  symbol:   class Box
  location: package com.flashphoner.media.mixer.video.presentation
com/flashphoner/mixerlayout/FullscreenLayout.java:40: error: cannot find symbol
        Box mainBox = new Box(null, canvasWidth, canvasHeight);
        ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:40: error: cannot find symbol
        Box mainBox = new Box(null, canvasWidth, canvasHeight);
                          ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:47: error: cannot find symbol
        Box container = new Box(mainBox, frameBoxWidth, canvasHeight);
        ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:47: error: cannot find symbol
        Box container = new Box(mainBox, frameBoxWidth, canvasHeight);
                            ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:48: error: cannot find symbol
        container.setPosition(BoxPosition.BOTTOM);
                              ^
  symbol:   variable BoxPosition
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:50: error: type List does not take parameters
            List<YUVFrame> FramesList = Arrays.asList(yuvFrames);
                ^
com/flashphoner/mixerlayout/FullscreenLayout.java:58: error: cannot find symbol
            Box frameBox = new Box(container, frameBoxWidth, canvasHeight);
            ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:58: error: cannot find symbol
            Box frameBox = new Box(container, frameBoxWidth, canvasHeight);
                               ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:59: error: cannot find symbol
            frameBox.setPosition(BoxPosition.INLINE_HORIZONTAL_CENTER);
                                 ^
  symbol:   variable BoxPosition
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:61: error: cannot find symbol
            Box frame = Box.computeBoxWithFrame(frameBox, yuvFrames[i]);
            ^
  symbol:   class Box
  location: class FullscreenLayout
com/flashphoner/mixerlayout/FullscreenLayout.java:61: error: cannot find symbol
            Box frame = Box.computeBoxWithFrame(frameBox, yuvFrames[i]);
                        ^
  symbol:   variable Box
  location: class FullscreenLayout
13 errors
 

Max

Administrator
Staff member
Пожалуйста, обновите сервер до последней сборки с этой страницы. Возможно, Вы используете сборку, где эти классы еще недоступны и обфусцированы. Проблема была исправлена в сборке 5.2.890.
 

fitsar

New Member
у меня есть микшер с двумя потоками
имя одного user_for второго user_against
задача в том что бы user_for всегда был в левой части экрана а user_against в правой

вот ой код
Java:
// Package name should be strictly defined as com.flashphoner.mixerlayout
package com.flashphoner.mixerlayout;

// Import mixer layout interface
import com.flashphoner.media.mixer.video.presentation.BoxPosition;
import com.flashphoner.sdk.media.IVideoMixerLayout;
// Import YUV frame description
import com.flashphoner.sdk.media.YUVFrame;
// Import Box class for picture operations
import com.flashphoner.media.mixer.video.presentation.Box;
// Import MixerConfig class
import com.flashphoner.server.commons.rmi.data.impl.MixerConfig;

// Import Java packages to use
import java.awt.*;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
* Custom mixer layout implementation example
*/
public class FullscreenLayout implements IVideoMixerLayout {

    // Pictures padding, should be even (or zero if no padding needed)
    private static final int PADDING = 0;

    /**
     * Function to compute layout, will be called by mixer before encoding output stream picture
     * This example computes one-line grid layout
     * @param yuvFrames - incoming streams raw pictures array in YUV format
     * @param strings - incoming streams names array
     * @param canvasWidth - mixer output picture canwas width
     * @param canvasHeight - mixer output picture canwas heigth
     * @return array of pictures layouts
     */
    @Override
    public Layout[] computeLayout(YUVFrame[] yuvFrames, String[] strings, int canvasWidth, int canvasHeight) {
        // This object represents mixer canvas
        Box mainBox = new Box(null, canvasWidth, canvasHeight);

        // Find every picture dimensions (this are the same for all the pictures because this is grid layout)
        int frameBoxWidth = canvasWidth / yuvFrames.length;

        // Container to place stream pictures
        Box container = new Box(mainBox, frameBoxWidth, canvasHeight);
        container.setPosition(BoxPosition.BOTTOM);
        if (strings[0].contains("user_aginsts")) {
            ArrayList<YUVFrame> FramesList = new ArrayList<>(Arrays.asList(yuvFrames));
            Collections.reverse(FramesList);
            yuvFrames = FramesList.toArray(new YUVFrame[FramesList.size()]);
        }

        for (int i = 0; i < yuvFrames.length; i++) {
            // This rectangle can include stream caption if mixer_text_outside_frame setting is enabled
            Box frameBox = new Box(container, frameBoxWidth, canvasHeight);
            frameBox.setPosition(BoxPosition.INLINE_HORIZONTAL_CENTER);
            // Compute picture frame placement including stream caption text
            Box frame = Box.computeBoxWithFrame(frameBox, yuvFrames[i]);
            // Adjust picture to the parent rectangle
            frame.fillParent();
        }
        // Prepare an array to return layout calculated
        ArrayList<IVideoMixerLayout.Layout> layout = new ArrayList<>();
        // Calculate mixer layout
        mainBox.computeLayout(layout);
        // Return the result
        return layout.toArray(new IVideoMixerLayout.Layout[layout.size()]);
    }

    /**
     * The function for internal use. Must be overriden.
     */
    @Override
    public void setConfig(MixerConfig mixerConfig) {
    }
}
дальше все делалось в соответствии с документацией

Code:
mkdir -p com/flashphoner/mixerlayout
aws s3 cp s3://some_bucket/flashphoner/FullscreenLayout.java com/flashphoner/mixerlayout
javac -cp /usr/local/FlashphonerWebCallServer/lib/wcs-core.jar ./com/flashphoner/mixerlayout/FullscreenLayout.java
jar -cf fullscreenlayout.jar ./com/flashphoner/mixerlayout/FullscreenLayout.class
cp fullscreenlayout.jar /usr/local/FlashphonerWebCallServer/lib
echo mixer_layout_class=com.flashphoner.mixerlayout.FullscreenLayout >> /usr/local/FlashphonerWebCallServer/conf/flashphoner.properties
но изображение по прежднему размещаются рандомно


это не приминился мой кастомный класс?

индекс изображение в yuvFrames соответствует индыску в strings?
 
Last edited:

Max

Administrator
Staff member
if (strings[0].contains("user_aginsts")) {
У вас опечатка в имени пользователя в коде, а имя второго пользователя нигде не проверяется и попадает в следующий кусок кода.
Кроме того, для Вашей задачи, если у Вас всего два пользователя, можно сделать проще, по аналоги с классом, пример которого приведен в этом сообщении, там требовалось вертикальное расположение.
Попробуйте прилагаемый пример кода
 

Attachments

fitsar

New Member
я применил код что выше
Code:
SettingsLoader - main Override setting mixer_layout_class: from com.flashphoner.media.mixer.video.presentation.GridLayout to com.flashphoner.mixerlayout.SideBySideLayout
но все равно положение участников на выходе в микшере рамндомное
при дисконекте одного участника тот что остался должен быть на весь экран ?
 

Max

Administrator
Staff member
но все равно положение участников на выходе в микшере рамндомное
при дисконекте одного участника тот что остался должен быть на весь экран ?
Похоже, лэйаут не применился при создании микшера. В предложенном коде поток, содержащий в имени user_for, будет всегда слева, user_against - справа. От потока с любым другим именем картинки в микшере не будет, только аудио.
Если лэйаут применяется, в серверном логе при создании микшера должна быть строка
Code:
09:59:36,067 INFO            VideoMixer - API-ASYNC-pool-13-thread-4 SideBySideLayout loaded for mixer video layout
Если там что-то другое, например ClassNotFoundException, убедитесь, что Вы скопировали jar файл в каталог /usr/local/FlashphonerWebCallServer/lib, выставили настройку и перезапустили после этого сервер.
Если все это сделано, но сообщения об успешной загрузке лэйаута в логе по-прежнему нет, предоставьте SSH доступы к серверу, используя эту форму
 

Max

Administrator
Staff member
На Вашем тестовом сервере лэйаут работает:
1. Публикуем потоки в любой последовательности:
Code:
ffmpeg -stream_loop -1 -re -y -rtbufsize 1k -i bunny360p.mp4 -preset ultrafast -vcodec h264 -acodec aac -ar 48000 -strict -2 -f flv -b:v 1M rtmp://**.***.***.***:1935/live/user_against#m1
ffmpeg -stream_loop -1 -re -y -rtbufsize 1k -i AudioVideoSyncTest.mp4 -preset ultrafast -vcodec h264 -acodec aac -ar 48000 -strict -2 -f flv -b:v 1M rtmp://**.***.***.***:1935/live/user_for#m1
2. Играем поток микшера в VLC
1617249710869.png

Для наглядности мы добавили настройку
Code:
mixer_display_stream_name=true
Остальные настройки на сервере оставлены, как есть.
Если подать на сервер только один поток, он будет на своем месте:
1617250021080.png

Итого: выглядит так, что лэйаут собран правильно и применяется.
Если он не применяется на каком-то сервере, просим дать доступ именно к этому серверу через форму с возможность публикации и проигрывания (на тестовом сервере был доступен только RTMP)
Убедитесь также, что Вы не пытаетесь запустить лэйаут, собранный, например, под Java 14, на Java 8
 

fitsar

New Member
Все работает , но есть нюанс
Раньше использовался класс layuot был com.flashphoner.media.mixer.video.presentation.CropNoPaddingGridLayout
Как совместить эти два класс ?
 

Max

Administrator
Staff member
Если Вы используете для разных микшеров разные типы лэйаутов, можно указывать лэйаут при создании микшера по REST API
Code:
POST /rest-api/mixer/startup HTTP/1.1
HOST: wcs:8081
content-type: application/json
 
{
    "uri": "mixer://mixer1",
    "localStreamName": "mixer1",
    "hasVideo": true,
    "hasAudio": true,
    "mixerLayoutClass": "com.flashphoner.media.mixer.video.presentation.CropNoPaddingGridLayout"
}
Микшеры, созданные автоматически при публикации потока с именем вида stream1#mixer1, будут использовать лэйаут, указанный в настройке сервера.
 

fitsar

New Member
мне нужно одновременно и com.flashphoner.media.mixer.video.presentation.CropNoPaddingGridLayout и SideBySideLayout, что бы и кроп был и расположение правильное
 

Max

Administrator
Staff member
Наш пример только показывает, как правильно расположить картинки потоков. Вы получаете картинку в YUV формате, и можете ее менять попиксельно, в том числе и получить кроп
Code:
        // Iterate through incoming stream pictures array
        for (int c = 0; c < yuvFrames.length; c++) {
            String name = strings[c];
            Box container;
            
            // Choose container depending on stream name
            if (name.contains(USERFOR)) {
                container = userForContainer;
            } else if (name.contains(USERAGAINST)) {
                container = userAgainstContainer;
            } else {
                // Wrong stream name
                continue;
            }
            // Fill the container by the stream picture
            Box frameBox = Box.computeBoxWithFrame(container, yuvFrames[c]);
            frameBox.fillParent();
        }
Здесь yuvFrames[c] - это картинка. Пример обработки декодированной картинки можно посмотреть здесь
Также существует тикет WCS-2988 по разработке языка разметки для лэйаутов, позволяющего обойтись без Java кода, возможно, подобный функционал будет реализован в рамках этого тикета.
 

fitsar

New Member
а можно увидеть пример как меняется кадр у класса com.flashphoner.media.mixer.video.presentation.CropNoPaddingGridLayout ?
 

Max

Administrator
Staff member
а можно увидеть пример как меняется кадр у класса com.flashphoner.media.mixer.video.presentation.CropNoPaddingGridLayout ?
Кроп делается в нативном коде, поэтому Вам придется реализовать его в Java коде с нуля. Если это сложно, лучше подождать реализации тикета WCS-2988
 
Top