Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@ void mirrors() throws Exception {
assertTrue(paint(view).getPixelReader().getColor(8, 24).getBrightness() > 0.75);
}

@Test
void anyRotationMultiple() throws Exception {
// A rotation outside 0..270 is shown as its equal inside it.
assertSamePixels(rotated(270), rotated(-90));
assertSamePixels(rotated(90), rotated(450));

// Sideways, a 4:3 picture in a 4:3 view leaves bars on the sides.
VideoView view = sized(64, 48);
view.accept(frame(64, 48, 128, 128, -90));
presented(view);

Image painted = paint(view);
assertEquals(Color.BLACK, painted.getPixelReader().getColor(2, 24));
assertGray(painted.getPixelReader().getColor(32, 24));
}

@Test
void showsRemoteVideo() throws Exception {
SignalingPair link = Signaling.loopback();
Expand Down Expand Up @@ -219,11 +235,24 @@ private static <T> T onFx(Callable<T> action) throws Exception {
return result.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}

/** What a 64x48 view paints of a 64x48 two-tone frame with the given rotation. */
private Image rotated(int rotation) throws Exception {
VideoView view = sized(64, 48);
view.accept(frame(64, 48, 16, 235, rotation));
presented(view);

return paint(view);
}

private static VideoFrame frame(int width, int height, int left, int right) {
return frame(width, height, left, right, 0);
}

/**
* A frame whose left half has luma {@code left} and right half {@code right}, with
* neutral chroma.
*/
private static VideoFrame frame(int width, int height, int left, int right) {
private static VideoFrame frame(int width, int height, int left, int right, int rotation) {
NativeI420Buffer buffer = NativeI420Buffer.allocate(width, height);
ByteBuffer y = buffer.getDataY();

Expand All @@ -236,7 +265,7 @@ private static VideoFrame frame(int width, int height, int left, int right) {
fill(buffer.getDataU());
fill(buffer.getDataV());

return new VideoFrame(buffer, System.nanoTime());
return new VideoFrame(buffer, rotation, System.nanoTime());
}

private static void fill(ByteBuffer plane) {
Expand All @@ -245,6 +274,18 @@ private static void fill(ByteBuffer plane) {
}
}

private static void assertSamePixels(Image expected, Image actual) {
assertEquals((int) expected.getWidth(), (int) actual.getWidth());
assertEquals((int) expected.getHeight(), (int) actual.getHeight());

for (int y = 0; y < (int) expected.getHeight(); y++) {
for (int x = 0; x < (int) expected.getWidth(); x++) {
assertEquals(expected.getPixelReader().getArgb(x, y), actual.getPixelReader().getArgb(x, y),
"Pixel " + x + "," + y);
}
}
}

private static void assertGray(Color color) {
assertNotNull(color);
assertTrue(Math.abs(color.getRed() - color.getGreen()) < 0.04 && Math.abs(color.getGreen() - color.getBlue()) < 0.04
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class VideoFrame implements RefCounted {
/** The underlying frame buffer. */
public final VideoFrameBuffer buffer;

/** Rotation of the frame in degrees. */
/** Clockwise rotation of the frame in degrees: 0, 90, 180 or 270. */
public final int rotation;

/** Timestamp of the frame in nanoseconds. */
Expand All @@ -55,7 +55,9 @@ public VideoFrame(VideoFrameBuffer buffer, long timestampNs) {
* Creates a new VideoFrame with the specified buffer, rotation and timestamp.
*
* @param buffer The video frame buffer containing the actual frame data.
* @param rotation The rotation of the frame in degrees (must be a multiple of 90).
* @param rotation The clockwise rotation of the frame in degrees (must be a multiple of 90).
* Any multiple is taken, and kept as its equal in 0, 90, 180 or 270, so that
* -90 and 450 are stored as 270 and 90.
* @param timestampNs The timestamp of the frame in nanoseconds.
*
* @throws IllegalArgumentException If buffer is null or rotation is not a multiple of 90.
Expand All @@ -69,7 +71,7 @@ public VideoFrame(VideoFrameBuffer buffer, int rotation, long timestampNs) {
}

this.buffer = buffer;
this.rotation = rotation;
this.rotation = ((rotation % 360) + 360) % 360;
this.timestampNs = timestampNs;
}

Expand Down
34 changes: 32 additions & 2 deletions webrtc/src/test/java/dev/onvoid/webrtc/client/VideoViewTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package dev.onvoid.webrtc.client;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
Expand Down Expand Up @@ -80,6 +81,22 @@ void mirrors() {
assertTrue(brightness(paint(view).getRGB(8, 24)) > 192);
}

@Test
void anyRotationMultiple() {
// A rotation outside 0..270 is shown as its equal inside it.
assertArrayEquals(rotated(270), rotated(-90));
assertArrayEquals(rotated(90), rotated(450));

// Sideways, a 4:3 picture in a 4:3 view leaves bars on the sides.
VideoView view = new VideoView();
view.setSize(64, 48);
view.accept(frame(64, 48, 128, 128, -90));

BufferedImage painted = paint(view);
assertEquals(Color.BLACK, new Color(painted.getRGB(2, 24)));
assertGray(new Color(painted.getRGB(32, 24)));
}

@Test
void showsRemoteVideo() throws Exception {
Peer[] peers = pair();
Expand Down Expand Up @@ -112,11 +129,24 @@ void showsRemoteVideo() throws Exception {
assertEquals(320, snapshot.getWidth());
}

/** The pixels a 64x48 view paints of a 64x48 two-tone frame with the given rotation. */
private static int[] rotated(int rotation) {
VideoView view = new VideoView();
view.setSize(64, 48);
view.accept(frame(64, 48, 16, 235, rotation));

return paint(view).getRGB(0, 0, 64, 48, null, 0, 64);
}

private static VideoFrame frame(int width, int height, int left, int right) {
return frame(width, height, left, right, 0);
}

/**
* A frame whose left half has luma {@code left} and right half {@code right}, with
* neutral chroma.
*/
private static VideoFrame frame(int width, int height, int left, int right) {
private static VideoFrame frame(int width, int height, int left, int right, int rotation) {
NativeI420Buffer buffer = NativeI420Buffer.allocate(width, height);
ByteBuffer y = buffer.getDataY();

Expand All @@ -129,7 +159,7 @@ private static VideoFrame frame(int width, int height, int left, int right) {
fill(buffer.getDataU());
fill(buffer.getDataV());

return new VideoFrame(buffer, System.nanoTime());
return new VideoFrame(buffer, rotation, System.nanoTime());
}

private static void fill(ByteBuffer plane) {
Expand Down
Loading