computeLayout
получает на вход список имен стримов и массив картинок в формате YUV, соответственно, Вы можете отсортировать список и разместить картинки с необходимой очередностью. Посмотрите также этот пост, там есть пример такого 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) {
}
}
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
// 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) {
}
}
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
У вас опечатка в имени пользователя в коде, а имя второго пользователя нигде не проверяется и попадает в следующий кусок кода.if (strings[0].contains("user_aginsts")) {
SettingsLoader - main Override setting mixer_layout_class: from com.flashphoner.media.mixer.video.presentation.GridLayout to com.flashphoner.mixerlayout.SideBySideLayout
Похоже, лэйаут не применился при создании микшера. В предложенном коде поток, содержащий в именино все равно положение участников на выходе в микшере рамндомное
при дисконекте одного участника тот что остался должен быть на весь экран ?
user_for
, будет всегда слева, user_against
- справа. От потока с любым другим именем картинки в микшере не будет, только аудио.09:59:36,067 INFO VideoMixer - API-ASYNC-pool-13-thread-4 SideBySideLayout loaded for mixer video layout
ClassNotFoundException
, убедитесь, что Вы скопировали jar файл в каталог /usr/local/FlashphonerWebCallServer/lib
, выставили настройку и перезапустили после этого сервер.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
mixer_display_stream_name=true
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
, будут использовать лэйаут, указанный в настройке сервера. // 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();
}
Кроп делается в нативном коде, поэтому Вам придется реализовать его в Java коде с нуля. Если это сложно, лучше подождать реализации тикета WCS-2988а можно увидеть пример как меняется кадр у класса com.flashphoner.media.mixer.video.presentation.CropNoPaddingGridLayout ?
Да, можетепо аналогии с этим примером я могу удалять пиксели ?