# Kontrolle über die Videodarstellung

Source: https://www.veeting.com/de/developer-documentation/video-display-calculator

## Übersicht

Mit dem Video Display Calculator ordnen Sie die Teilnehmervideos selbst an. Statt unser Layout zu übernehmen, schreiben Sie eine Funktion. Diese bekommt die Containergrösse und die Teilnehmer und gibt für jedes Video eine absolute Position zurück.

Der Calculator gehört zur [JavaScript-API](/de/developer-documentation/javascript-apis). Alles, was auf jener Seite dazu steht, wie Sie den Handler erhalten, gilt deshalb auch hier.

| Begriff   | Bedeutung                                                           |
| --------- | ------------------------------------------------------------------- |
| Video     | Der Videostream eines Teilnehmers                                   |
| Container | Der Bereich des Meetingraums, in dem der Raum alle Videos darstellt |

## Der primäre und der sekundäre Bereich

Das Präsentationslayout teilt den Container in zwei Bereiche. Das klassische Layout kennt keinen sekundären Bereich. Dort füllt der primäre Bereich immer den ganzen Container.

Standardmässig liegt jedes Video im primären Bereich, und dieser nimmt den ganzen Container ein. Ein Moderator kann Videos in den sekundären Bereich schieben. Sobald dort ein Video liegt, teilt sich der Container.

![Primary and secondary area](/assets/img/documentation/video-display-calculator-areas.png)

Der sekundäre Bereich kann an jeder Kante liegen und kommt in zwei Grössen:

| Wert                | Bedeutung                                                                        |
| ------------------- | -------------------------------------------------------------------------------- |
| `secondaryPosition` | `none`, `left`, `top`, `right` oder `bottom`                                     |
| `secondarySize`     | `0.15` für den kleinen, `0.5` für den grossen Bereich, als Anteil des Containers |

Sie bekommen beide Werte. Ob Sie sie beachten, entscheiden Sie selbst, und wie Sie den Platz aufteilen, ebenfalls. Wir liefern Ihnen die Masse des gesamten Containers in Pixeln, nicht die der einzelnen Bereiche.

## Den Calculator schreiben

Der Meetingraum ruft Ihre Funktion immer dann auf, wenn er die Positionen neu berechnen muss: wenn jemand beitritt oder geht, wenn sich die Fenstergrösse ändert, wenn der Moderator den Raum umstellt. Ihre Funktion muss synchron zurückkehren.

Der Meetingraum hat **12 Videocontainer**. Sie entscheiden, welche davon Sie nutzen und welche verborgen bleiben.

### Was Sie erhalten

Die Parameter zählen nach Position, nicht nach Namen. Die Namen wählen Sie also selbst. In dieser Reihenfolge kommen sie an:

| Position | Parameter                    | Typ                           | Bedeutung                                           |
| -------- | ---------------------------- | ----------------------------- | --------------------------------------------------- |
| 1        | containerWidth               | number                        | Breite des Videocontainers, in Pixeln               |
| 2        | containerHeight              | number                        | Höhe des Videocontainers, in Pixeln                 |
| 3        | hasSecondaryArea             | boolean                       | Ob der sekundäre Bereich sichtbar ist               |
| 4        | secondaryPosition            | SecondaryVideoPosition        | An welcher Kante der sekundäre Bereich liegt        |
| 5        | secondarySize                | SecondaryVideoSize            | `0.15` oder `0.5`                                   |
| 6        | primaryParticipants          | string[]                      | Teilnehmer-IDs im primären Bereich                  |
| 7        | secondaryParticipants        | string[]                      | Teilnehmer-IDs im sekundären Bereich                |
| 8        | participantsOrder            | string[]                      | Alle Teilnehmer-IDs, in der Reihenfolge der Anzeige |
| 9        | participantsMediaInformation | IParticipantsMediaInformation | Medienzustand und Videomasse pro Teilnehmer         |
| 10       | hasSelfView                  | boolean                       | Ob es einen Self-View gibt                          |
| 11       | isSelfviewInSecondary        | boolean                       | Ob der Self-View in den sekundären Bereich gehört   |
| 12       | isTVMode                     | boolean                       | Ob der Raum im TV-Modus läuft                       |

> **Der Self-View steht in keinem Teilnehmer-Array.** Ist `hasSelfView` gleich `true` und wollen Sie den Self-View zeigen, dann ergänzen Sie selbst eine Position für die Teilnehmer-ID `myself`. Sonst tut es niemand.

### Was Sie zurückgeben

| Eigenschaft      | Typ              | Bedeutung                                                                         |
| ---------------- | ---------------- | --------------------------------------------------------------------------------- |
| videoPositions   | IVideoPosition[] | Ein Eintrag je Video, das der Raum darstellen soll                                |
| hiddenContainers | boolean[]        | 12 Einträge. `false` zeigt den Container an dieser Position, `true` verbirgt ihn. |

Die beiden Arrays hängen über den Index zusammen: Der erste Eintrag von `videoPositions` gilt für Container `0`, der zweite für Container `1` und so weiter. **Eine Position, deren `hiddenContainers[i]` auf `true` bleibt, erscheint nicht.** Das ist hier der mit Abstand häufigste Fehler.

Eine `IVideoPosition` sieht so aus:

| Eigenschaft   | Typ      | Bedeutung                                                                |
| ------------- | -------- | ------------------------------------------------------------------------ |
| participantId | string   | Wessen Video in diesen Container gehört, oder `myself` für den Self-View |
| width         | number   | Breite in Pixeln                                                         |
| height        | number   | Höhe in Pixeln                                                           |
| top           | number   | Abstand vom oberen Rand des Containers, in Pixeln                        |
| left          | number   | Abstand vom linken Rand des Containers, in Pixeln                        |
| zIndex        | number   | Stapelreihenfolge, falls Sie Videos überlappen lassen                    |
| cssClasses    | string[] | Optional. **Jede Klasse muss mit `video-container-` beginnen.**          |

### Wenn Ihr Calculator scheitert

Wirft er einen Fehler oder gibt er ein leeres Objekt zurück, fällt der Meetingraum stillschweigend auf das eingebaute Layout zurück. Niemand bekommt eine Fehlermeldung zu sehen. Erscheint Ihr Layout also nicht, schauen Sie zuerst in der Browserkonsole nach.

## Beispiel

Das folgende Beispiel setzt den ersten Teilnehmer in die Mitte, die übrigen in eine Reihe darüber und den Self-View darunter.

![Example video display](/assets/img/documentation/video-display-calculator-example.png)

Es nutzt nur den primären Bereich.

```javascript
const videoDisplayCalculator = {
  calculatePositions: (containerWidth,
    containerHeight,
    hasSecondaryArea,
    secondaryPosition,
    secondarySize,
    primaryParticipants,
    secondaryParticipants,
    participantsOrder,
    participantsMediaInformation,
    hasSelfView,
    isSelfviewInSecondary,
    isTVMode) => {

    const result = {
      videoPositions: [],
      hiddenContainers: Array(12).fill(true)
    };

    // We want to display the videos with a 4:3 format
    const threeToFour = 3 / 4;
    // The main video is placed in the center, 65% of the container size
    const mainContainerSizePercentage = 0.65;

    let mainContainerWidth = 0;
    let mainContainerHeight = 0;
    let mainContainerTop = 0;
    let mainContainerLeft = 0;

    if (Array.isArray(participantsOrder) && participantsOrder.length > 0) {
      mainContainerWidth = containerWidth * mainContainerSizePercentage;
      mainContainerHeight = mainContainerWidth * threeToFour;
      if (mainContainerHeight > containerHeight * mainContainerSizePercentage) {
        mainContainerHeight = containerHeight * mainContainerSizePercentage;
        mainContainerWidth = mainContainerHeight / threeToFour;
      }

      mainContainerTop = (containerHeight - mainContainerHeight) / 2;
      mainContainerLeft = (containerWidth - mainContainerWidth) / 2;

      // Adding the main video
      result.videoPositions.push({
        participantId: participantsOrder[0],
        width: mainContainerWidth,
        height: mainContainerHeight,
        top: mainContainerTop,
        left: mainContainerLeft,
        zIndex: 1
      });
      result.hiddenContainers[0] = false;

      const numberOfOtherVideos = participantsOrder.length - 1;

      if (numberOfOtherVideos > 0) {
        // Adding all other videos in the top row
        let otherContainersWidth = containerWidth / numberOfOtherVideos;
        let otherContainersHeight = otherContainersWidth * threeToFour;
        if (otherContainersHeight > mainContainerTop) {
          otherContainersHeight = mainContainerTop;
          otherContainersWidth = otherContainersHeight / threeToFour;
        }

        const otherContainersTop = (mainContainerTop - otherContainersHeight) / 2;
        let otherContainersLeft = (containerWidth / 2)
          - ((otherContainersWidth * numberOfOtherVideos) / 2);

        for (let i = 1; i < participantsOrder.length; i++) {
          result.videoPositions.push({
            participantId: participantsOrder[i],
            width: otherContainersWidth,
            height: otherContainersHeight,
            top: otherContainersTop,
            left: otherContainersLeft,
            zIndex: 1
          });
          result.hiddenContainers[i] = false;

          otherContainersLeft += otherContainersWidth;
        }
      }
    }

    if (hasSelfView) {
      // The self view is never in participantsOrder, so add it explicitly.
      const selfViewHeight = containerHeight - mainContainerTop - mainContainerHeight;
      const selfViewWidth = selfViewHeight / threeToFour;

      result.videoPositions.push({
        participantId: "myself",
        width: selfViewWidth,
        height: selfViewHeight,
        top: containerHeight - selfViewHeight,
        left: (containerWidth / 2) - (selfViewWidth / 2),
        zIndex: 1
      });

      // Reveal the container this position was pushed into. Without this the
      // self view is calculated correctly and then never shown.
      result.hiddenContainers[result.videoPositions.length - 1] = false;
    }

    return result;
  }
};

window["wlvmrApiReady"] = (handler) => {
  if (!window[handler]) {
    console.error(`Handler window.${handler} not found!`);
    return;
  }
  window[handler].setVideoDisplayCalculator(videoDisplayCalculator);
};
```

## Typdefinitionen

```typescript
type SecondaryVideoPosition = "none" | "left" | "top" | "right" | "bottom";

enum SecondaryVideoSize {
  small = 0.15,
  large = 0.5
}

interface IVideoPosition {
  participantId: string;
  width: number;
  height: number;
  top: number;
  left: number;
  zIndex: number;
  cssClasses?: string[];
}

interface IVideoDisplayConfig {
  videoPositions: IVideoPosition[];
  hiddenContainers: boolean[];
}

interface IMediaInformation {
  audioMuted?: boolean;
  videoMuted?: boolean;
  hasAudio?: boolean;
  hadAudio?: boolean;
  hasVideo?: boolean;
  // Bad connectivity sometimes stops the video: hasVideo is false and
  // hadVideo is true, which means we expect it to come back.
  hadVideo?: boolean;
  videoWidth?: number;
  videoHeight?: number;
}

interface IParticipantsMediaInformation {
  [participantId: string]: IMediaInformation;
}
```

## Häufige Fehler

- Eine Position hinzufügen und den zugehörigen Eintrag in `hiddenContainers` auf `true` stehen lassen. Das Video erscheint dann nie.
- Den Self-View vergessen. Er steht in keinem Teilnehmer-Array, Sie müssen ihn als `myself` ergänzen.
- Eine Fehlermeldung erwarten, wenn der Calculator scheitert. Er fällt stillschweigend auf das eingebaute Layout zurück.
- Mehr als 12 Positionen zurückgeben.
- Eine CSS-Klasse verwenden, die nicht mit `video-container-` beginnt.
- Annehmen, `secondarySize` sei immer `0.15`. Beim grossen sekundären Bereich ist er `0.5`.
- Asynchron arbeiten. Die Funktion muss ihr Ergebnis synchron zurückgeben.

---

## Die übrige Dokumentation

- [Custom Tools – eigene Widgets im Meetingraum](https://www.veeting.com/de/developer-documentation/custom-tools)
- [Externer Autorisierungsdienst](https://www.veeting.com/de/developer-documentation/external-meeting-authorization-service)
- [iFrame und Web Komponenten](https://www.veeting.com/de/developer-documentation/iframe-and-web-components)
- [JavaScript APIs](https://www.veeting.com/de/developer-documentation/javascript-apis)
- [JavaScript und Typescript APIs](https://www.veeting.com/de/veeting-blocks/apis)
- [Komponenten](https://www.veeting.com/de/veeting-blocks/components)
- [Parameter in der URL](https://www.veeting.com/de/developer-documentation/query-parameters)
- [Veeting Blocks - Übersicht](https://www.veeting.com/de/veeting-blocks/introduction)
- [Veeting Rooms REST APIs](https://www.veeting.com/de/developer-documentation/api-usage)
- [Webhooks](https://www.veeting.com/de/developer-documentation/web-hooks)

Alles in einer Datei: https://www.veeting.com/llms-full.txt
