Skip to content

Совместный просмотр

Совместный просмотр позволяет участникам звонка смотреть видео вместе с синхронизацией воспроизведения.

Жизненный цикл

┌─────────────────┐
│   addMovie()    │  ← Добавить видео
└────────┬────────┘

┌────────▼────────┐
│ onSharedMovieInfo │  ← Получить информацию о видео
└────────┬────────┘

┌────────▼────────┐
│updateDisplayLayout│  ← Запросить MediaStream
└────────┬────────┘

┌────────▼────────┐
│    onLive       │  ← Получить MediaStream
└────────┬────────┘

    Воспроизведение
    (onLiveUpdate)

┌────────▼────────┐
│  removeMovie()  │  ← Удалить видео
└─────────────────┘

Добавление видео

javascript
// Добавить видео по ID
await SDK.addMovie({
    movieId: 123456,
    gain: 0.5  // Начальная громкость (0-1)
});

Метаданные

Желательно передать IMovieMetaData в SDK.addMovie. В противном случае бекенд попробует получить её сам, но результат не всегда будет предсказуемым.

Получение информации о видео

После добавления видео все участники получают уведомление:

javascript
await SDK.init({
    // ...
    
    // Для инициатора
    onLocalSharedMovieInfo: (userId, movieInfo, roomId) => {
        console.log('Видео добавлено:', movieInfo.title);
        console.log('Movie ID:', movieInfo.movieId);
        
        // Запросить MediaStream
        requestMovieStream(userId, movieInfo);
    },
    
    // Для остальных участников
    onRemoteSharedMovieInfo: (userId, movieInfo, roomId) => {
        console.log('Участник добавил видео:', movieInfo.title);
        
        // Запросить MediaStream
        requestMovieStream(userId, movieInfo);
    }
});

Запрос MediaStream

После получения информации о видео нужно запросить поток:

javascript
import { MediaType } from '@vkontakte/calls-sdk';

function requestMovieStream(userId, movieInfo) {
    SDK.updateDisplayLayout([{
        uid: userId,
        mediaType: MediaType.MOVIE,
        width: 1920,
        height: 1080,
        streamName: movieInfo.movieId.toString()
    }]);
}

Получение MediaStream

javascript
await SDK.init({
    // ...
    
    // Для инициатора
    onLocalLive: (userId, data) => {
        if (data.stream) {
            videoElement.srcObject = data.stream;
        }
    },
    
    // Для остальных участников
    onRemoteLive: (userId, data) => {
        if (data.stream && data.mediaType === MediaType.MOVIE) {
            videoElement.srcObject = data.stream;
        }
    }
});

Управление воспроизведением

Пауза и воспроизведение

javascript
// Поставить на паузу
await SDK.updateMovie({
    movieId: activeMovieId,
    pause: true
});

// Возобновить воспроизведение
await SDK.updateMovie({
    movieId: activeMovieId,
    pause: false
});

Управление громкостью

javascript
// Установить громкость (0-1)
await SDK.updateMovie({
    movieId: activeMovieId,
    gain: 0.8  // 80%
});

// Выключить звук
await SDK.updateMovie({
    movieId: activeMovieId,
    mute: true
});

// Включить звук
await SDK.updateMovie({
    movieId: activeMovieId,
    mute: false
});

Перемотка

javascript
// Перемотать на определённое время (в секундах)
await SDK.updateMovie({
    movieId: activeMovieId,
    offset: 120  // К 2-й минуте
});

Отслеживание изменений состояния

Изменения состояния видео приходят в коллбэках:

javascript
await SDK.init({
    // ...
    
    // Для инициатора
    onLocalLiveUpdate: (userId, data) => {
        console.log('Громкость:', data.gain);
        console.log('На паузе:', data.pause);
        console.log('Звук выключен:', data.mute);
        console.log('Позиция:', data.offset);
    },
    
    // Для остальных участников
    onRemoteLiveUpdate: (userId, data) => {
        updatePlayerUI(data);
    }
});

Удаление видео

javascript
// Удалить видео
await SDK.removeMovie(movieId);

После удаления участники получают уведомление:

javascript
await SDK.init({
    // ...
    
    onLocalSharedMovieStoppedInfo: (movieInfo, roomId) => {
        // Видео удалено (для инициатора)
        stopMovieStream();
    },
    
    onRemoteSharedMovieStoppedInfo: (userId, movieInfo, roomId) => {
        // Видео удалено другим участником
        stopMovieStream();
    }
});

function stopMovieStream() {
    // Остановить поток
    SDK.updateDisplayLayout([{
        uid: userId,
        mediaType: MediaType.MOVIE,
        stopStream: true
    }]);
    
    videoElement.srcObject = null;
}

Управление доступом

Можно ограничить управление видео только для администраторов:

javascript
import { ConversationFeature, UserRole } from '@vkontakte/calls-sdk';

// Разрешить управление только админам и создателю
await SDK.enableFeatureForRoles(
    ConversationFeature.MOVIE_SHARE,
    [UserRole.CREATOR, UserRole.ADMIN]
);

// Разрешить всем
await SDK.enableFeatureForRoles(
    ConversationFeature.MOVIE_SHARE,
    []
);

Полный пример

javascript
import * as SDK from '@vkontakte/calls-sdk';
import { MediaType } from '@vkontakte/calls-sdk';

let activeMovieId = null;
let movieParticipantId = null;

await SDK.init({
    // ...
    
    onLocalSharedMovieInfo: (userId, movieInfo, roomId) => {
        activeMovieId = movieInfo.movieId;
        movieParticipantId = userId;
        requestMovieStream(userId, movieInfo);
    },
    
    onRemoteSharedMovieInfo: (userId, movieInfo, roomId) => {
        activeMovieId = movieInfo.movieId;
        movieParticipantId = userId;
        requestMovieStream(userId, movieInfo);
    },
    
    onLocalLive: (userId, data) => {
        if (data.stream) {
            playMovie(data.stream);
        }
    },
    
    onRemoteLive: (userId, data) => {
        if (data.stream && data.mediaType === MediaType.MOVIE) {
            playMovie(data.stream);
        }
    },
    
    onLocalLiveUpdate: (userId, data) => updateUI(data),
    onRemoteLiveUpdate: (userId, data) => updateUI(data),
    
    onLocalSharedMovieStoppedInfo: () => stopMovie(),
    onRemoteSharedMovieStoppedInfo: () => stopMovie()
});

function requestMovieStream(userId, movieInfo) {
    SDK.updateDisplayLayout([{
        uid: userId,
        mediaType: MediaType.MOVIE,
        width: 1920,
        height: 1080,
        streamName: movieInfo.movieId.toString()
    }]);
}

function playMovie(stream) {
    const video = document.getElementById('movie-player');
    video.srcObject = stream;
    video.play();
}

function updateUI(data) {
    // Обновить UI плеера
    document.getElementById('volume').value = data.gain * 100;
    document.getElementById('pause-btn').textContent = data.pause ? '▶️' : '⏸️';
}

function stopMovie() {
    activeMovieId = null;
    const video = document.getElementById('movie-player');
    video.srcObject = null;
}

// Управление
async function togglePause() {
    if (!activeMovieId) return;
    
    const isPaused = document.getElementById('pause-btn').textContent === '▶️';
    await SDK.updateMovie({
        movieId: activeMovieId,
        pause: !isPaused
    });
}

async function setVolume(value) {
    if (!activeMovieId) return;
    
    await SDK.updateMovie({
        movieId: activeMovieId,
        gain: value / 100
    });
}

Особенности

Права на управление

По умолчанию управлять видео может только тот, кто его добавил, и администраторы звонка.

Важно

Не забудьте вызвать updateDisplayLayout с stopStream: true после удаления видео, чтобы корректно освободить ресурсы.