Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

REST-methods and response statuses

REST-method

Example of REST query

Example of response

Response statuses

Description

/mixer/startup

Code Block
languagejs
themeRDark
{
  "uri": "mixer://mixer1",
  "localStreamName": "stream3",
  "hasVideo": "true",
  "hasAudio": "true",
  "watermark": "watermark.png",
  "background": "background.png",
  "mixerLayoutClass": "com.flashphoner.mixerlayout.TestLayout"
}

200 - OK

400 - Bad request

409 - Conflict

500 - Internal error

Creates a mixer the provided stream is published for

/mixer/add

Code Block
languagejs
themeRDark
{
  "uri": "mixer://mixer1",
  "remoteStreamName": "stream1",
  "hasVideo": "true",
  "hasAudio": "true"
}

200 - OK

404 - Mixer not found

404 - Stream not found

500 - Internal error

Add the RTMP stream to the mixer

/mixer/remove

Code Block
languagejs
themeRDark
{
  "uri": "mixer://mixer1",
  "remoteStreamName": "stream1"
}

200 - OK

404 - Mixer not found

404 - Stream not found

500 - Internal error

Remove the RTMP stream from the mixer

/mixer/find_all


Code Block
languagejs
themeRDark
{
"localMediaSessionId": "ce92b134-2468-4460-8d06-1ea3c5aabace",
"remoteMediaSessionId": null,
"localStreamName": "mixer1",
"remoteStreamName": null,
"uri": "mixer://mixer1",
"status": "PROCESSED_LOCAL",
"mediaSessions": [
  "95bf2be8-f459-4f62-9a7f-c588f33e0ad3",
  "693781de-cada-4589-abe1-c3ee55c66901"
],
}

200 - OK

404 - Not found

500 - Internal error

Find all mixers

/mixer/terminate

Code Block
languagejs
themeRDark
{
  "uri": "mixer://mixer1"
}

200 - OK

404 - Not found

500 - Internal error

Terminate operation of the mixer

/stream/startRecording

Code Block
languagejs
themeRDark
{
  "mediaSessionId": "23d07fa1-3c74-4d6f-a0de-9b4bf83ce665"
}

200 - OK

404 - Not found

500 - Internal error

Start recording of the stream in the given media session

/stream/stopRecording

Code Block
languagejs
themeRDark
{
  "mediaSessionId": "23d07fa1-3c74-4d6f-a0de-9b4bf83ce665"
}

200 - OK

404 - Not found

500 - Internal error

Stop recording the stream in the given media session

/mixer/setAudioVideo

Code Block
languagejs
themeRDark
{
 "uri": "mixer://m1",
 "streams": "^stream.*",
 "audioLevel": 0,
 "videoMuted": true
}

200 - OK

400 - Bad request

404 - Not found

500 - Internal error

Mute/unmute video or change audio level for mexer incoming stream

Parameters

...

Parameter name

...

Description

...

Example

...

uri

...

Unique identifier of the mixer

...

mixer://mixer1

...

localStreamName

...

Name of the output stream of the mixer

...

stream3

...

remoteStreamName

...

Name of the stream added to the mixer

...

stream1

rtmp://rtmp.flashphoner.com:1935/live/rtmp_stream1

...

mediaSessionId

...

Media session identifier

...

ce92b134-2468-4460-8d06-1ea3c5aabace

...

status

...

Stream status

...

PROCESSED_LOCAL

...

^stream.*

["stream1", "stream2"]

...

Mixer configuration while creating the instance using REST API

Since build 5.2.872 it is possible to pass most of mixer parameters corresponding to flashphoner.properties mixer settings while creating the mixer using /mixer/startup REST query. In this case, parameters will be applied to the created mixer instance only. For example, the following query

Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1",
 "localStreamName": "stream3",
 "mixerVideoWidth": 640,
 "mixerVideoHeight": 360,
 "mixerVideoFps": 24,
 "mixerVideoBitrateKbps": 500
}

will create the mixer with output stream resolution 640x360, fps 24 and bitrate 500 kbps, no matter what settings are set in flashphoner.properties file

The mixer parameters fukll list can be retrieved using /mixer/find_all REST query, for example

...

languagejs
themeRDark
titleMixer parameters full list
collapsetrue

...

/mixer/set_body_watermark
Code Block
languagejs
themeRDark
{
 "uri":"mixer://m1",
 "watermark":"/opt/media/logo.png",
 "x":10,
 "y":10,
 "marginTop":5,
 "marginLeft":5,
 "marginBottom":5,
 "marginRight":5
}

200 - OK

400 - Bad request

404 - Not found

Add watermark to mixer output stream picture
/mixer/set_stream_watermark
Code Block
languagejs
themeRDark
{
 "uri":"mixer://m1",
 "watermark":"/opt/media/logo.png",
 "mediaSessionId": "030bb470-185c-11ed-9fad-918e05233ae9",
 "x":10,
 "y":10,
 "marginTop":5,
 "marginLeft":5,
 "marginBottom":5,
 "marginRight":5
}

200 - OK

400 - Bad request

404 - Not found

Add watermark to one of the mixer input streams pictures in the mixer output stream

Parameters

Parameter name

Description

Example

uri

Unique identifier of the mixer

mixer://mixer1

localStreamName

Name of the output stream of the mixer

stream3

hasVideoMix videotrue
hasAudioMix audiotrue

remoteStreamName

Name of the stream added to the mixer

stream1

rtmp://rtmp.flashphoner.com:1935/live/rtmp_stream1

mediaSessionId

Media session identifier

ce92b134-2468-4460-8d06-1ea3c5aabace

status

Stream status

PROCESSED_LOCAL

backgroundMixer backgroundbackground.png
watermarkMixer watermarkwatermark.png
mixerLayoutClassMixer layoutcom.flashphoner.mixerlayout.TestLayout
streamsStreams list or regular expression for search

^stream.*

["stream1", "stream2"]

audioLevelIncoming stream audio level0
videoMutedMute videotrue

Mixer configuration while creating the instance using REST API

Since build 5.2.872 it is possible to pass most of mixer parameters corresponding to flashphoner.properties mixer settings while creating the mixer using /mixer/startup REST query. In this case, parameters will be applied to the created mixer instance only. For example, the following query

Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1",
 "localStreamName": "stream3",
 "mixerVideoWidth": 640,
 "mixerVideoHeight": 360,
 "mixerVideoFps": 24,
 "mixerVideoBitrateKbps": 500
}

will create the mixer with output stream resolution 640x360, fps 24 and bitrate 500 kbps, no matter what settings are set in flashphoner.properties file

The mixer parameters fukll list can be retrieved using /mixer/find_all REST query, for example

Code Block
languagejs
themeRDark
titleMixer parameters full list
collapsetrue
[
  {
    "localMediaSessionId": "7c9e5353-8680-4ad2-8a47-1a366091785c",
    "localStreamName": "m1",
    "uri": "mixer://m1",
    "status": "PROCESSED_LOCAL",
    "hasAudio": true,
    "hasVideo": true,
    "record": false,
    "mediaSessions": [],
    "mixerLayoutClass": "com.flashphoner.media.mixer.video.presentation.GridLayout",
    "mixerLayoutDir": "/opt/mixer1-layout",
    "mixerActivityTimerCoolOffPeriod": 1,
    "mixerActivityTimerTimeout": -1,
    "mixerAppName": "defaultApp",
    "mixerAudioOpusFloatCoding": false,
    "mixerOutBufferInitialSizemixerAudioSilenceThreshold": 2000-50,
    "mixerOutBufferStartSizemixerAudioThreads": 1504,
    "mixerOutBufferPollingTimemixerAutoScaleDesktop": 100true,
    "mixerOutBufferMaxBufferingsAllowedmixerDebugMode": -1false,
    "mixerShowSeparateAudioFramemixerDesktopAlign": true"TOP",
    "mixerTextAutoscalemixerDisplayStreamName": truefalse,
    "mixerTextColourmixerFontSize": "0xFFFFFF"20,
    "mixerTextBulkWriteWithBuffermixerFontSizeAudioOnly": true40,
    "mixerTextBulkWritemixerIdleTimeout": true20000,
    "mixerTextBackgroundOpacitymixerInBufferingMs": 100200,
    "mixerTextBackgroundColourmixerIncomingTimeRateLowerThreshold": "0x2B2A2B"0.95,
    "mixerTextPaddingLeftmixerIncomingTimeRateUpperThreshold": 51.05,
    "mixerVoiceActivitySwitchDelaymixerMcuAudio": 0false,
    "mixerVoiceActivityFrameThicknessmixerMcuVideo": 6false,
    "mixerVoiceActivityFramePositionInnermixerMcuMultithreadedMix": false,
    "mixerVoiceActivityColourmixerMinimalFontSize": "0x00CC66"1,
    "mixerVoiceActivitymixerMcuMultithreadedDelivery": truefalse,
    "mixerVideoWidthmixerOutBufferEnabled": 1280false,
    "mixerVideoThreadsmixerOutBufferInitialSize": 42000,
    "mixerVideoStableFpsThresholdmixerOutBufferStartSize": 15150,
    "mixerVideoQualitymixerOutBufferPollingTime": 24100,
    "mixerVideoProfileLevelmixerOutBufferMaxBufferingsAllowed": "42c02a"-1,
    "mixerVideoLayoutDesktopKeyWordmixerShowSeparateAudioFrame": "desktop"true,
    "mixerVideoHeightmixerTextAutoscale": 720true,
    "mixerVideoGridLayoutPaddingmixerTextColour": 30"0xFFFFFF",
    "mixerVideoGridLayoutMiddlePaddingmixerTextBulkWriteWithBuffer": 10true,
    "mixerVideoFpsmixerTextBulkWrite": 30true,
    "mixerVideoDesktopLayoutPaddingmixerTextBackgroundOpacity": 30100,
    "mixerVideoDesktopLayoutInlinePaddingmixerTextBackgroundColour": 10"0x2B2A2B",
    "mixerVideoBufferLengthmixerTextPaddingLeft": 10005,
    "mixerVoiceActivitySwitchDelay": 0,
    "mixerVideoBitrateKbpsmixerVoiceActivityFrameThickness": 20006,
    "mixerUseSdpStatemixerVoiceActivityFramePositionInner": truefalse,
    "mixerTypemixerVoiceActivityColour": "NATIVE0x00CC66",
    "mixerThreadTimeoutMsmixerVoiceActivity": 33true,
    "mixerTextPaddingTopmixerVideoWidth": 51280,
    "mixerTextPaddingRightmixerVideoThreads": 4,
    "mixerTextFontmixerVideoStableFpsThreshold": "Serif"15,
    "mixerVideoQuality": 24,
    "mixerTextPaddingBottommixerVideoProfileLevel": 5"42c02a",
    "mixerTextDisplayRoommixerVideoLayoutDesktopKeyWord": true"desktop",
    "mixerTextCutTopmixerVideoHeight": 3720,
    "mixerRealtimemixerVideoGridLayoutPadding": true30,
    "mixerPruneStreamsmixerVideoGridLayoutMiddlePadding": false10,
    "audioMixerOutputCodecmixerVideoFps": "alaw"30,
    "audioMixerOutputSampleRatemixerVideoDesktopLayoutPadding": 4800030,
    "audioMixerMaxDelaymixerVideoDesktopLayoutInlinePadding": 30010,
    "mixerAudioOnlyHeightmixerVideoBufferLength": 3601000,
    "mixerAudioOnlyWidthmixerVideoBitrateKbps": 6402000,
    "videoOutputCodecmixerUseSdpState":"vp8" true,
      "mixerTextAlignmixerType": "TOP_CENTERNATIVE",
  }
    "mixerThreadTimeoutMs": 33,
    "mixerTextPaddingTop": 5,
    "mixerTextPaddingRight": 4,
    "mixerTextFont": "Serif",
    "mixerTextPaddingBottom": 5,
    "mixerTextDisplayRoom": true,
    "mixerTextCutTop": 3,
    "mixerRealtime": true,
    "mixerPruneStreams": false,
    "audioMixerOutputCodec": "alaw",
    "audioMixerOutputSampleRate": 48000,
    "audioMixerMaxDelay": 300,
    "mixerAudioOnlyHeight": 360,
    "mixerAudioOnlyWidth": 640,
    "videoOutputCodec":"vp8",
    "mixerTextAlign": "TOP_CENTER"
  }
]

Sending the REST query to the WCS server

...

Code Block
languagejava
themeRDark
titleBoxPosition
collapsetrue
public enum BoxPosition {
    //attach box to the left
    LEFT,

    //attach box to the right
    RIGHT,

    //attach box to the top
    TOP,

    //attach box to the bottom
    BOTTOM,

    //center box in the parent box
    CENTER,

    //bottom and center box in the parent box
    BOTTOM_CENTER,

    //top and center box in the parent box
    TOP_CENTER,

    //attach box to the right-top corner of left adjoining box (in the same parent box)
    INLINE_HORIZONTAL,

    //same as INLINE_HORIZONTAL but additionally adds vertical CENTER
    INLINE_HORIZONTAL_CENTER,

    //attach box to the left-bottom corner of upper adjoining box (in the same parent box)
    INLINE_VERTICAL,

    //same as INLINE_VERTICAL but additionally adds horizontal CENTER
    INLINE_VERTICAL,

    //same as INLINE_VERTICAL but additionally adds horizontal CENTER
    INLINE_VERTICAL_CENTER
}

Then the class should be complied into byte code. To do this, create folder tree accordind to TestLayout class package name

Code Block
languagebash
themeRDark
mkdir -p com/flashphoner/mixerlayout

and execute the command

Code Block
languagebash
themeRDark
javac -cp /usr/local/FlashphonerWebCallServer/lib/wcs-core.jar ./com/flashphoner/mixerlayout/TestLayout.java

Now, pack the code compiled to jar file

Code Block
languagebash
themeRDark
jar -cf testlayout.jar ./com/flashphoner/mixerlayout/TestLayout.class

and copy this file to WCS libraries folder

Code Block
languagebash
themeRDark
cp testlayout.jar /usr/local/FlashphonerWebCallServer/lib

To use custom mixer layout class, set it to the following parameter in flashphoner.properties file

Code Block
themeRDark
mixer_layout_class=com.flashphoner.mixerlayout.TestLayout

and restart WCS.

With this custom layout, mixer output stream for three input streams will look like:

Image Removed

Cropping pictures in custom layout

To crop a picture around a central point like CropNoPaddingGridLayout, use Box.fillParentNoScale() instead of Box.fillParent() method. The following example places two pictures by name with cropping around center:

Code Block
languagejava
themeRDark
titleSideBySideLayout.java
collapsetrue
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;
// Uncomment this if using build 5.2.878-5.2.976
// import com.flashphoner.server.commons.rmi.data.impl.MixerConfig;

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

public class SideBySideLayout implements IVideoMixerLayout {
    // Owner's stream name
    private static final String USERFOR = "user_for";
    // Challenger's stream name
    private static final String USERAGAINST = "user_against";

    /**
     * Function to compute layout, will be called by mixer before encoding output stream picture
     * This example computes 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);
_CENTER
}

Then the class should be complied into byte code. To do this, create folder tree accordind to TestLayout class package name

Code Block
languagebash
themeRDark
mkdir -p com/flashphoner/mixerlayout

and execute the command

Code Block
languagebash
themeRDark
javac -cp /usr/local/FlashphonerWebCallServer/lib/wcs-core.jar ./com/flashphoner/mixerlayout/TestLayout.java

Now, pack the code compiled to jar file

Code Block
languagebash
themeRDark
jar -cf testlayout.jar ./com/flashphoner/mixerlayout/TestLayout.class

and copy this file to WCS libraries folder

Code Block
languagebash
themeRDark
cp testlayout.jar /usr/local/FlashphonerWebCallServer/lib

To use custom mixer layout class, set it to the following parameter in flashphoner.properties file

Code Block
themeRDark
mixer_layout_class=com.flashphoner.mixerlayout.TestLayout

and restart WCS.

With this custom layout, mixer output stream for three input streams will look like:

Image Added

Cropping pictures in custom layout

To crop a picture around a central point like CropNoPaddingGridLayout, use Box.fillParentNoScale() instead of Box.fillParent() method. The following example places two pictures by name with cropping around center:

Code Block
languagejava
themeRDark
titleSideBySideLayout.java
collapsetrue
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;
// Uncomment this if using build 5.2.878-5.2.976
// import com.flashphoner.server.commons.rmi.data.impl.MixerConfig;

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

public class SideBySideLayout implements IVideoMixerLayout {
    // Owner's stream name
    private static final String USERFOR = "user_for";
    // Challenger's stream name
    private static final String USERAGAINST = "user_against";

    /**
     * Function to compute layout, will be called by mixer before encoding output stream picture
     * This example computes 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);

        // Container to place CHALLENGER stream pictures
        Box userForContainer = new Box(mainBox, canvasWidth / 2, canvasHeight);
        userForContainer.setPosition(BoxPosition.LEFT);
        // Container to place OWNER stream pictures
        Box userAgainstContainer = new Box(mainBox, canvasWidth / 2, canvasHeight);
        userAgainstContainer.setPosition(BoxPosition.RIGHT);

        // Iterate through incoming stream pictures array
        for (int c = 0; c < yuvFrames.length; c++) {
            String name = strings[c];
        // Container to place CHALLENGER stream picturesBox container;
        Box userForContainer = new Box(mainBox, canvasWidth / 2, canvasHeight);
        userForContainer.setPosition(BoxPosition.LEFT);
        // ContainerChhose tocontainer placedepending OWNERon stream picturesname
         Box userAgainstContainer = new Box(mainBox, canvasWidth / 2, canvasHeight);if (name.contains(USERFOR)) {
        userAgainstContainer.setPosition(BoxPosition.RIGHT);

        // Iterate through incoming streamcontainer pictures= arrayuserForContainer;
        for (int c = 0;} celse <if yuvFrames.length; c++(name.contains(USERAGAINST)) {
            String    namecontainer = strings[c]userAgainstContainer;
            Box container;} else {
            
    // Wrong stream name
     // Chhose container depending on stream name
     continue;
       if (name.contains(USERFOR)) {
   }
            // containerFill =the userForContainer;
container by the stream picture
        } else if (name.contains(USERAGAINST)) {
  Box frameBox = Box.computeBoxWithFrame(container, yuvFrames[c]);
          container = userAgainstContainerframeBox.fillParentNoScale();
            } else {
        
        // WrongPrepare streaman name
array to return layout calculated
        ArrayList<IVideoMixerLayout.Layout> layout = new continueArrayList<>();
        // Calculate mixer  }layout
        mainBox.computeLayout(layout);
    //  Fill the container// byReturn the stream pictureresult
            Box frameBox = Box.computeBoxWithFrame(container, yuvFrames[creturn layout.toArray(new IVideoMixerLayout.Layout[layout.size()]);
        
    frameBox.fillParentNoScale();
}
    
    /**
 }    * The function for internal use.
     * Uncomment this if //using Prepare an array to return layout calculatedbuild 5.2.878-5.2.976
     */
    //@Override
    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.
     * Uncomment this if using build 5.2.878-5.2.976
     */
    //@Override
    //public void setConfig(MixerConfig mixerConfig) {
    //}
}

Mixer layout management while creating mixer

...

//public void setConfig(MixerConfig mixerConfig) {
    //}
}

Mixer layout management while creating mixer

Since build 5.2.693 mixer layout can be defined when creating mixer with REST API, for example

Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1",
 "localStreamName": "mixer1",
 "mixerLayoutClass": "com.flashphoner.mixerlayout.TestLayout"
}

Then, layout can be defib=ned for every mixer separately

Mixer output stream encoding profile management

Some browsers do not support playback for H264 streams encoded by certain profiles. To solve it, the following parameter is added since build 5.2.414 to set mixer output stream encoding profile

Code Block
themeRDark
mixer_video_profile_level=42c02a

By default, constrainted baseline level 4.2 profile is set.

Mixer background management and watermarking

Since build 5.2.693 mixer backgroung can be defined and watermark can be added when creating mixer with REST API, for example

Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1",
 "localStreamName": "mixer1",
 "watermark": "watermark.png",
 "background": "background.png"
}

By default, files should be placed to /usr/local/FlashphonerWebCallServer/conf folder. Full path to the files can also be set, for example

Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1",
 "localStreamName": "mixer1",
 "watermark": "/opt/media/watermark.png",
 "background": "/opt/media/background.png"
}

Adding and changing stream watermark dynamically

Since build 5.2.1349 in is possible to dynamically add or change stream watermark without stopping the mixer. A watermark can be added, changed or moved to another picture location according to coordinates defined using REST API queries:

  • /mixer/set_body_watermark - to the whole mixer output stream picture
Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1m1",
 "localStreamNamewatermark":"/opt/media/logo.png",
 "mixer1x":0,
 "mixerLayoutClassy":0,
 "com.flashphoner.mixerlayout.TestLayout"
}

Then, layout can be defib=ned for every mixer separately

Mixer output stream encoding profile management

Some browsers do not support playback for H264 streams encoded by certain profiles. To solve it, the following parameter is added since build 5.2.414 to set mixer output stream encoding profile

Code Block
themeRDark
mixer_video_profile_level=42c02a

By default, constrainted baseline level 4.2 profile is set.

Mixer background management and watermarking

...

marginTop":0,
 "marginLeft":0,
 "marginBottom":0,
 "marginRight":0
}

Image Added

  • /mixer/set_stream_watermark - to a certain input stream picture
Code Block
languagejs
themeRDark
{
 "uri":"mixer://m1",
 "watermark"mixer:"/opt//mixer1media/logo.png",
 "mediaSessionId": "030bb470-185c-11ed-9fad-918e05233ae9",
 "localStreamNamex":0,
 "mixer1y":0,
 "watermarkmarginTop":0,
 "watermark.pngmarginLeft":0,
 "backgroundmarginBottom":0,
 "background.png"
}

...

marginRight":0
}

Image Added

Where

  • watermark - watermark file name
  • x, y - top left watermark corner coordinates on the stream picture
  • marginTop, marginLeft, marginBottom, marginRignt - watermark margins from stream picture borders

If watermark coordinates are out of stream picture bounds, the watermark will be scaled to the bounds using margins.

To move watermark to another location on the stream picture, send the query with the same file name and a new coordinates. To remove watermark from the stream picture, send the query with empty watermark field

Code Block
languagejs
themeRDark
{
 "uri": "mixer://mixer1m1",
 "localStreamName": "mixer1",
 "watermark": "/opt/media/watermark.png",
 "background": "/opt/media/background.png"
}

Stereo sound in mixer output stream

...