|
1 | | -import { beforeAll, describe, expect, test, vi } from 'vitest'; |
2 | | -import mp4box from '@webav/mp4box.js'; |
3 | 1 | import { autoReadStream, file2stream } from '@webav/internal-utils'; |
| 2 | +import mp4box from '@webav/mp4box.js'; |
4 | 3 | import { file, write } from 'opfs-tools'; |
5 | | -import { quickParseMP4File } from '../mp4box-utils'; |
| 4 | +import { beforeAll, describe, expect, test, vi } from 'vitest'; |
| 5 | +import { |
| 6 | + createVFRotater, |
| 7 | + parseMatrix, |
| 8 | + quickParseMP4File, |
| 9 | +} from '../mp4box-utils'; |
6 | 10 |
|
7 | 11 | beforeAll(() => { |
8 | 12 | vi.useFakeTimers(); |
@@ -95,3 +99,118 @@ test('quickParseMP4File', async () => { |
95 | 99 | expect(sampleCount).toBe(40); |
96 | 100 | await reader.close(); |
97 | 101 | }); |
| 102 | + |
| 103 | +test('vfRotater can be rotate VideoFrame instance', () => { |
| 104 | + const vf = new VideoFrame(new Uint8Array(200 * 100 * 4), { |
| 105 | + codedHeight: 100, |
| 106 | + codedWidth: 200, |
| 107 | + format: 'RGBA', |
| 108 | + timestamp: 0, |
| 109 | + }); |
| 110 | + |
| 111 | + // Test 90 degree rotation |
| 112 | + const rotater90 = createVFRotater(200, 100, 90); |
| 113 | + const rotatedVF90 = rotater90(vf.clone()); |
| 114 | + expect(rotatedVF90).not.toBeNull(); |
| 115 | + if (rotatedVF90 == null) throw new Error('must not be null'); |
| 116 | + expect(rotatedVF90.codedWidth).toBe(100); |
| 117 | + expect(rotatedVF90.codedHeight).toBe(200); |
| 118 | + rotatedVF90.close(); |
| 119 | + |
| 120 | + // Test 180 degree rotation |
| 121 | + const rotater180 = createVFRotater(200, 100, 180); |
| 122 | + const rotatedVF180 = rotater180(vf.clone()); |
| 123 | + expect(rotatedVF180).not.toBeNull(); |
| 124 | + if (rotatedVF180 == null) throw new Error('must not be null'); |
| 125 | + expect(rotatedVF180.codedWidth).toBe(200); |
| 126 | + expect(rotatedVF180.codedHeight).toBe(100); |
| 127 | + rotatedVF180.close(); |
| 128 | + |
| 129 | + // Test 270 degree rotation |
| 130 | + const rotater270 = createVFRotater(200, 100, 270); |
| 131 | + const rotatedVF270 = rotater270(vf.clone()); |
| 132 | + expect(rotatedVF270).not.toBeNull(); |
| 133 | + if (rotatedVF270 == null) throw new Error('must not be null'); |
| 134 | + expect(rotatedVF270.codedWidth).toBe(100); |
| 135 | + expect(rotatedVF270.codedHeight).toBe(200); |
| 136 | + rotatedVF270.close(); |
| 137 | + |
| 138 | + // Test 0 degree rotation |
| 139 | + const rotater0 = createVFRotater(200, 100, 0); |
| 140 | + const vfClone = vf.clone(); |
| 141 | + const rotatedVF0 = rotater0(vfClone); |
| 142 | + // For 0 rotation, it should return the original frame |
| 143 | + expect(rotatedVF0).toBe(vfClone); |
| 144 | + rotatedVF0?.close(); |
| 145 | + |
| 146 | + vf.close(); |
| 147 | +}); |
| 148 | + |
| 149 | +describe('parseMatrix', () => { |
| 150 | + test('should throw error for invalid matrix length', () => { |
| 151 | + const matrix = new Int32Array(8); |
| 152 | + expect(parseMatrix(matrix)).toEqual({}); |
| 153 | + }); |
| 154 | + |
| 155 | + test('should parse 0 degree rotation matrix', () => { |
| 156 | + const matrix = new Int32Array([65536, 0, 0, 0, 65536, 0, 0, 0, 1073741824]); |
| 157 | + const result = parseMatrix(matrix); |
| 158 | + expect(result.rotationDeg).toBe(0); |
| 159 | + expect(result.scaleX).toBe(1); |
| 160 | + expect(result.scaleY).toBe(1); |
| 161 | + expect(result.translateX).toBe(0); |
| 162 | + expect(result.translateY).toBe(0); |
| 163 | + }); |
| 164 | + |
| 165 | + test('should parse 90 degree rotation matrix', () => { |
| 166 | + // matrix for 90 deg rotation |
| 167 | + const matrix = new Int32Array([ |
| 168 | + 0, 65536, 0, -65536, 0, 0, 0, 0, 1073741824, |
| 169 | + ]); |
| 170 | + const result = parseMatrix(matrix); |
| 171 | + expect(result.rotationDeg).toBe(-90); |
| 172 | + expect(result.scaleX).toBe(1); |
| 173 | + expect(result.scaleY).toBe(1); |
| 174 | + }); |
| 175 | + |
| 176 | + test('should parse 180 degree rotation matrix', () => { |
| 177 | + const matrix = new Int32Array([ |
| 178 | + -65536, 0, 0, 0, -65536, 0, 0, 0, 1073741824, |
| 179 | + ]); |
| 180 | + const result = parseMatrix(matrix); |
| 181 | + expect(result.rotationDeg).toBe(180); |
| 182 | + expect(result.scaleX).toBe(1); |
| 183 | + expect(result.scaleY).toBe(1); |
| 184 | + }); |
| 185 | + |
| 186 | + test('should parse 270 degree rotation matrix', () => { |
| 187 | + const matrix = new Int32Array([ |
| 188 | + 0, -65536, 0, 65536, 0, 0, 0, 0, 1073741824, |
| 189 | + ]); |
| 190 | + const result = parseMatrix(matrix); |
| 191 | + expect(result.rotationDeg).toBe(90); |
| 192 | + expect(result.scaleX).toBe(1); |
| 193 | + expect(result.scaleY).toBe(1); |
| 194 | + }); |
| 195 | + |
| 196 | + test('should parse matrix with translation', () => { |
| 197 | + const width = 1920; |
| 198 | + const height = 1080; |
| 199 | + // 180 deg rotation + translation |
| 200 | + const matrix = new Int32Array([ |
| 201 | + -65536, |
| 202 | + 0, |
| 203 | + 0, |
| 204 | + 0, |
| 205 | + -65536, |
| 206 | + 0, |
| 207 | + width * 65536, |
| 208 | + height * 65536, |
| 209 | + 1073741824, |
| 210 | + ]); |
| 211 | + const result = parseMatrix(matrix); |
| 212 | + expect(result.rotationDeg).toBe(180); |
| 213 | + expect(result.translateX).toBe(width); |
| 214 | + expect(result.translateY).toBe(height); |
| 215 | + }); |
| 216 | +}); |
0 commit comments