mirror of https://github.com/OpenVidu/openvidu.git
openvidu-test-e2e: add replaceTrack test
parent
9fa6898db2
commit
e6ea4ee1d8
|
@ -86,6 +86,14 @@ public class RecordingUtils {
|
|||
&& (Math.abs(rgb.get("b") - rgb.get("g")) <= 2);
|
||||
}
|
||||
|
||||
public static boolean checkVideoAverageRgbLightGray(Map<String, Long> rgb) {
|
||||
// GRAY color: {r < 50, g < 50, b < 50} and the absolute difference between them
|
||||
// not greater than 2
|
||||
return (rgb.get("r") >= 90) && (rgb.get("g") >= 90) && (rgb.get("b") >= 90) && (rgb.get("r") <= 110)
|
||||
&& (rgb.get("g") <= 110) && (rgb.get("b") <= 110) && (Math.abs(rgb.get("r") - rgb.get("g")) <= 10)
|
||||
&& (Math.abs(rgb.get("r") - rgb.get("b")) <= 10) && (Math.abs(rgb.get("b") - rgb.get("g")) <= 10);
|
||||
}
|
||||
|
||||
public static boolean checkVideoAverageRgbRed(Map<String, Long> rgb) {
|
||||
// RED color: {r > 240, g < 15, b <15}
|
||||
return (rgb.get("r") > 240) && (rgb.get("g") < 15) && (rgb.get("b") < 15);
|
||||
|
|
|
@ -809,6 +809,49 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
|||
gracefullyLeaveParticipants(user, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Replace track")
|
||||
void replaceTrackTest() throws Exception {
|
||||
|
||||
OpenViduTestappUser user = setupBrowserAndConnectToOpenViduTestapp("chrome");
|
||||
|
||||
log.info("Replace track");
|
||||
|
||||
WebElement oneToManyInput = user.getDriver().findElement(By.id("one2many-input"));
|
||||
oneToManyInput.clear();
|
||||
oneToManyInput.sendKeys("1");
|
||||
|
||||
user.getDriver().findElement(By.id("auto-join-checkbox")).click();
|
||||
user.getDriver().findElement(By.id("one2many-btn")).click();
|
||||
|
||||
user.getEventManager().waitUntilEventReaches("streamCreated", 2);
|
||||
user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(2);
|
||||
user.getEventManager().on("streamPropertyChanged", (event) -> {
|
||||
if ("videoTrack".equals(event.get("changedProperty").getAsString())) {
|
||||
Assertions.assertEquals("trackReplaced", event.get("reason").getAsString(),
|
||||
"Wrong streamPropertyChanged reason for videoTrack");
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .replace-track-btn")).click();
|
||||
if (!latch.await(3000, TimeUnit.MILLISECONDS)) {
|
||||
gracefullyLeaveParticipants(user, 2);
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
WebElement publisherVideo = user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 video"));
|
||||
WebElement subscriberVideo = user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 video"));
|
||||
Map<String, Long> rgbPublisher = user.getBrowserUser().getAverageRgbFromVideo(publisherVideo);
|
||||
Map<String, Long> rgbSubscriber = user.getBrowserUser().getAverageRgbFromVideo(subscriberVideo);
|
||||
Assertions.assertTrue(RecordingUtils.checkVideoAverageRgbLightGray(rgbPublisher),
|
||||
"Publisher video is not average gray");
|
||||
Assertions.assertTrue(RecordingUtils.checkVideoAverageRgbLightGray(rgbSubscriber),
|
||||
"Subscriber video is not average gray");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Moderator capabilities")
|
||||
void moderatorCapabilitiesTest() throws Exception {
|
||||
|
|
|
@ -617,53 +617,6 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
|||
this.subscribers.push(session.subscribe(event.stream, undefined));
|
||||
}
|
||||
|
||||
initGrayVideo(): void {
|
||||
|
||||
this.OV.getUserMedia(
|
||||
{
|
||||
audioSource: undefined,
|
||||
videoSource: undefined,
|
||||
resolution: '1280x720',
|
||||
frameRate: 3,
|
||||
}
|
||||
)
|
||||
.then((mediaStream: MediaStream) => {
|
||||
|
||||
const videoStreamTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0];
|
||||
const video: HTMLVideoElement = document.createElement('video');
|
||||
video.srcObject = new MediaStream([videoStreamTrack]);
|
||||
video.play();
|
||||
const canvas = document.createElement('canvas') as any;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.filter = 'grayscale(100%)';
|
||||
|
||||
video.addEventListener('play', () => {
|
||||
const loop = () => {
|
||||
if (!video.paused && !video.ended) {
|
||||
ctx.drawImage(video, 0, 0, 300, 170);
|
||||
setTimeout(loop, 33); // Drawing at 30fps
|
||||
}
|
||||
};
|
||||
loop();
|
||||
});
|
||||
const grayVideoTrack: MediaStreamTrack = (<MediaStream>canvas.captureStream(30)).getVideoTracks()[0];
|
||||
this.OV.getUserMedia({
|
||||
audioSource: false,
|
||||
videoSource: grayVideoTrack
|
||||
}).then(mediastream => {
|
||||
this.publisher.replaceTrack(mediastream.getVideoTracks()[0])
|
||||
.then(() => console.log('New track is being published'))
|
||||
.catch(error => {
|
||||
console.error('Error replacing track');
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
openSessionPropertiesDialog() {
|
||||
this.sessionProperties.customSessionId = this.sessionName;
|
||||
const dialogRef = this.dialog.open(SessionPropertiesDialogComponent, {
|
||||
|
|
|
@ -30,7 +30,10 @@
|
|||
<mat-icon aria-label="Publish or unpublish audio" class="mat-icon material-icons" role="img" aria-hidden="true">{{pubSubAudioIcon}}</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="!this.unpublished" class="video-btn change-publisher-btn" title="Change publisher" (click)="changePub()">
|
||||
<mat-icon aria-label="Change publisher" class="mat-icon material-icons" role="img" aria-hidden="true">switch_video</mat-icon>
|
||||
<mat-icon aria-label="Change publisher" class="mat-icon material-icons" role="img" aria-hidden="true">cameraswitch</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="!this.unpublished" class="video-btn replace-track-btn" title="Replace track" (click)="replaceTrack()">
|
||||
<mat-icon aria-label="Replace track" class="mat-icon material-icons" role="img" aria-hidden="true">switch_video</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="!this.unpublished" class="video-btn reconnect-publisher-btn" title="Reconnect publisher" (click)="reconnect()">
|
||||
<mat-icon aria-label="Reconnect publisher" class="mat-icon material-icons" role="img" aria-hidden="true">refresh</mat-icon>
|
||||
|
|
|
@ -46,6 +46,7 @@ export class VideoComponent implements OnInit, OnDestroy {
|
|||
|
||||
unpublished = false;
|
||||
publisherChanged = false;
|
||||
trackReplaced = false;
|
||||
audioMuted = false;
|
||||
videoMuted = false;
|
||||
sendAudio = true;
|
||||
|
@ -321,6 +322,42 @@ export class VideoComponent implements OnInit, OnDestroy {
|
|||
this.publisherChanged = !this.publisherChanged;
|
||||
}
|
||||
|
||||
async replaceTrack() {
|
||||
let newMediaStream: MediaStream;
|
||||
let newVideoTrack: MediaStreamTrack;
|
||||
const originalPublisherProperties = this.streamManager.stream.outboundStreamOpts.publisherProperties;
|
||||
if (!this.trackReplaced) {
|
||||
newMediaStream = await this.OV.getUserMedia(originalPublisherProperties);
|
||||
const videoStreamTrack: MediaStreamTrack = newMediaStream.getVideoTracks()[0];
|
||||
const video: HTMLVideoElement = document.createElement('video');
|
||||
video.srcObject = new MediaStream([videoStreamTrack]);
|
||||
video.play();
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.filter = 'grayscale(100%)';
|
||||
video.addEventListener('play', () => {
|
||||
const loop = () => {
|
||||
if (!video.paused && !video.ended) {
|
||||
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth, video.videoHeight);
|
||||
setTimeout(loop, 33); // Drawing at 30fps
|
||||
}
|
||||
};
|
||||
loop();
|
||||
});
|
||||
newVideoTrack = canvas.captureStream(30).getVideoTracks()[0];
|
||||
} else {
|
||||
newMediaStream = await this.OV.getUserMedia(originalPublisherProperties);
|
||||
newVideoTrack = newMediaStream.getVideoTracks()[0];
|
||||
}
|
||||
try {
|
||||
await (this.streamManager as Publisher).replaceTrack(newVideoTrack);
|
||||
console.log('Track replaced');
|
||||
} catch (error) {
|
||||
console.error('Error replacing track', error);
|
||||
}
|
||||
this.trackReplaced = !this.trackReplaced;
|
||||
}
|
||||
|
||||
reconnect() {
|
||||
this.streamManager.stream.reconnect()
|
||||
.then(() => console.log(`Stream ${this.streamManager.stream} (${this.streamManager.remote ? 'Subscriber' : 'Publisher'}) successfully reconnected`))
|
||||
|
|
Loading…
Reference in New Issue