diff --git a/tools/winscope-ng/src/common/utils/array_utils.spec.ts b/tools/winscope-ng/src/common/utils/array_utils.spec.ts index f52898951..f1e880154 100644 --- a/tools/winscope-ng/src/common/utils/array_utils.spec.ts +++ b/tools/winscope-ng/src/common/utils/array_utils.spec.ts @@ -81,15 +81,74 @@ describe("ArrayUtils", () => { it("toUintLittleEndian", () => { const buffer = new Uint8Array([0, 0, 1, 1]); - expect(ArrayUtils.toUintLittleEndian(buffer, 0, -1)).toEqual(0); - expect(ArrayUtils.toUintLittleEndian(buffer, 0, 0)).toEqual(0); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, -1)).toEqual(0n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, 0)).toEqual(0n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 1, 1)).toEqual(0n); - expect(ArrayUtils.toUintLittleEndian(buffer, 0, 1)).toEqual(0); - expect(ArrayUtils.toUintLittleEndian(buffer, 0, 2)).toEqual(0); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 0, 1)).toEqual(0n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 1, 2)).toEqual(1n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01, 0xff]), 2, 3)).toEqual(255n); - expect(ArrayUtils.toUintLittleEndian(buffer, 3, 4)).toEqual(1); - expect(ArrayUtils.toUintLittleEndian(buffer, 2, 4)).toEqual(1 + 256); - expect(ArrayUtils.toUintLittleEndian(buffer, 1, 4)).toEqual(256 + 256*256); - expect(ArrayUtils.toUintLittleEndian(buffer, 0, 3)).toEqual(256*256); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x00]), 0, 2)).toEqual(0n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x01, 0x00]), 0, 2)).toEqual(1n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0x00, 0x01]), 0, 2)).toEqual(256n); + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff]), 0, 2)).toEqual(0xffffn); + + expect(ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff]), 0, 4)).toEqual(0xffffffffn); + + expect( + ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 8)) + .toEqual(0xffffffffffffffffn); + + expect( + ArrayUtils.toUintLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 9)) + .toEqual(0xffffffffffffffffffn); + }); + + it("toIntLittleEndian", () => { + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, -1)).toEqual(0n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, 0)).toEqual(0n); + + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00]), 0, 1)).toEqual(0n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01]), 0, 1)).toEqual(1n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x7f]), 0, 1)).toEqual(127n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x80]), 0, 1)).toEqual(-128n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff]), 0, 1)).toEqual(-1n); + + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0x7f]), 0, 2)).toEqual(32767n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x80]), 0, 2)).toEqual(-32768n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x80]), 0, 2)).toEqual(-32767n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff]), 0, 2)).toEqual(-1n); + + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0x7f]), 0, 4)).toEqual(0x7fffffffn); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x80]), 0, 4)).toEqual(-0x80000000n); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x80]), 0, 4)).toEqual(-0x7fffffffn); + expect(ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff]), 0, 4)).toEqual(-1n); + + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]), 0, 8) + ).toEqual(0x7fffffffffffffffn); + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 8) + ).toEqual(-0x8000000000000000n); + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 8) + ).toEqual(-0x7fffffffffffffffn); + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 8) + ).toEqual(-1n); + + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]), 0, 9) + ).toEqual(0x7fffffffffffffffffn); + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 9) + ).toEqual(-0x800000000000000000n); + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), 0, 9) + ).toEqual(-0x7fffffffffffffffffn); + expect( + ArrayUtils.toIntLittleEndian(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 0, 9) + ).toEqual(-1n); }); }); diff --git a/tools/winscope-ng/src/common/utils/array_utils.ts b/tools/winscope-ng/src/common/utils/array_utils.ts index 60bcc3223..b9a54689b 100644 --- a/tools/winscope-ng/src/common/utils/array_utils.ts +++ b/tools/winscope-ng/src/common/utils/array_utils.ts @@ -86,14 +86,30 @@ class ArrayUtils { return result; } - static toUintLittleEndian(buffer: Uint8Array, start: number, end: number) { - let result = 0; - for (let i = end-1; i>=start; --i) { - result *= 256; - result += buffer[i]; + static toUintLittleEndian(buffer: Uint8Array, start: number, end: number): bigint { + let result = 0n; + for (let i = end-1; i >= start; --i) { + result *= 256n; + result += BigInt(buffer[i]); } return result; } + + static toIntLittleEndian(buffer: Uint8Array, start: number, end: number): bigint { + const numOfBits = BigInt(Math.max(0, 8 * (end-start))); + if (numOfBits <= 0n) { + return 0n; + } + + let result = ArrayUtils.toUintLittleEndian(buffer, start, end); + const maxSignedValue = 2n ** (numOfBits - 1n) - 1n; + if (result > maxSignedValue) { + const valuesRange = 2n ** numOfBits; + result -= valuesRange; + } + + return result; + } } export {ArrayUtils}; diff --git a/tools/winscope-ng/src/parsers/parser_factory.ts b/tools/winscope-ng/src/parsers/parser_factory.ts index 449685717..fac3ba087 100644 --- a/tools/winscope-ng/src/parsers/parser_factory.ts +++ b/tools/winscope-ng/src/parsers/parser_factory.ts @@ -20,6 +20,7 @@ import {ParserInputMethodManagerService} from "./parser_input_method_manager_ser import {ParserInputMethodService} from "./parser_input_method_service"; import {ParserProtoLog} from "./parser_protolog"; import {ParserScreenRecording} from "./parser_screen_recording"; +import {ParserScreenRecordingLegacy} from "./parser_screen_recording_legacy"; import {ParserSurfaceFlinger} from "./parser_surface_flinger"; import {ParserTransactions} from "./parser_transactions"; import {ParserWindowManager} from "./parser_window_manager"; @@ -33,6 +34,7 @@ class ParserFactory { ParserInputMethodService, ParserProtoLog, ParserScreenRecording, + ParserScreenRecordingLegacy, ParserSurfaceFlinger, ParserTransactions, ParserWindowManager, diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts b/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts index c65e80787..4d8b5e2f7 100644 --- a/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts +++ b/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts @@ -23,8 +23,8 @@ describe("ParserScreenRecording", () => { let parser: Parser; beforeAll(async () => { - const buffer = TestUtils.getFixtureBlob("screen_recording.mp4"); - const parsers = await new ParserFactory().createParsers([buffer]); + const trace = TestUtils.getFixtureBlob("screen_recording.mp4"); + const parsers = await new ParserFactory().createParsers([trace]); expect(parsers.length).toEqual(1); parser = parsers[0]; }); @@ -37,26 +37,23 @@ describe("ParserScreenRecording", () => { const timestamps = parser.getTimestamps(); expect(timestamps.length) - .toEqual(85); + .toEqual(88); expect(timestamps.slice(0, 3)) - .toEqual([19446131807000, 19446158500000, 19446167117000]); - - expect(timestamps.slice(timestamps.length-3, timestamps.length)) - .toEqual([19448470076000, 19448487525000, 19448501007000]); + .toEqual([1658843852566916400, 1658843852889741300, 1658843852901528300]); }); it("retrieves trace entry", () => { { - const entry = parser.getTraceEntry(19446131807000)!; + const entry = parser.getTraceEntry(1658843852566916400)!; expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry); expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0); } { - const entry = parser.getTraceEntry(19448501007000)!; + const entry = parser.getTraceEntry(1658843852889741300)!; expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry); - expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001); + expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0.322, 0.001); } }); }); diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording.ts b/tools/winscope-ng/src/parsers/parser_screen_recording.ts index 465120f07..99ee2b2b5 100644 --- a/tools/winscope-ng/src/parsers/parser_screen_recording.ts +++ b/tools/winscope-ng/src/parsers/parser_screen_recording.ts @@ -18,6 +18,11 @@ import {ArrayUtils} from "common/utils/array_utils"; import {Parser} from "./parser"; import {ScreenRecordingTraceEntry} from "common/trace/screen_recording"; +class ScreenRecordingMetadataEntry { + constructor(public timestampMonotonicNs: bigint, public timestampRealtimeNs: bigint) { + } +} + class ParserScreenRecording extends Parser { constructor(trace: Blob) { super(trace); @@ -31,20 +36,29 @@ class ParserScreenRecording extends Parser { return ParserScreenRecording.MPEG4_MAGIC_NMBER; } - override decodeTrace(videoData: Uint8Array): number[] { - const posCount = this.searchMagicString(videoData); - const [posTimestamps, count] = this.parseTimestampsCount(videoData, posCount); - return this.parseTimestamps(videoData, posTimestamps, count); + override decodeTrace(videoData: Uint8Array): ScreenRecordingMetadataEntry[] { + const posVersion = this.searchMagicString(videoData); + const [posTimeOffset, metadataVersion] = this.parseMetadataVersion(videoData, posVersion); + if (metadataVersion !== 1) { + throw TypeError(`Metadata version "${metadataVersion}" not supported`); + } + const [posCount, timeOffsetNs] = this.parseRealToMonotonicTimeOffsetNs(videoData, posTimeOffset); + const [posTimestamps, count] = this.parseFramesCount(videoData, posCount); + const timestampsMonotonicNs = this.parseTimestampsMonotonicNs(videoData, posTimestamps, count); + + return timestampsMonotonicNs.map((timestampMonotonicNs: bigint) => { + return new ScreenRecordingMetadataEntry(timestampMonotonicNs, timestampMonotonicNs + timeOffsetNs); + }); } - override getTimestamp(decodedEntry: number): number { - return decodedEntry; + override getTimestamp(decodedEntry: ScreenRecordingMetadataEntry): number { + return Number(decodedEntry.timestampRealtimeNs); } - override processDecodedEntry(timestamp: number): ScreenRecordingTraceEntry { - const videoTimeSeconds = (timestamp - this.timestamps[0]) / 1000000000 + ParserScreenRecording.EPSILON; + override processDecodedEntry(entry: ScreenRecordingMetadataEntry): ScreenRecordingTraceEntry { + const videoTimeSeconds = (Number(entry.timestampRealtimeNs) - this.timestamps[0]) / 1000000000; const videoData = this.trace; - return new ScreenRecordingTraceEntry(timestamp, videoTimeSeconds, videoData); + return new ScreenRecordingTraceEntry(Number(entry.timestampRealtimeNs), videoTimeSeconds, videoData); } private searchMagicString(videoData: Uint8Array): number { @@ -56,22 +70,42 @@ class ParserScreenRecording extends Parser { return pos; } - private parseTimestampsCount(videoData: Uint8Array, pos: number) : [number, number] { - if (pos + 4 >= videoData.length) { - throw new TypeError("video data is too short. Expected timestamps count doesn't fit"); + private parseMetadataVersion(videoData: Uint8Array, pos: number) : [number, number] { + if (pos + 4 > videoData.length) { + throw new TypeError("Failed to parse metadata version. Video data is too short."); } - const timestampsCount = ArrayUtils.toUintLittleEndian(videoData, pos, pos+4); + const version = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+4)); pos += 4; - return [pos, timestampsCount]; + return [pos, version]; } - private parseTimestamps(videoData: Uint8Array, pos: number, count: number): number[] { - if (pos + count * 8 >= videoData.length) { - throw new TypeError("video data is too short. Expected timestamps do not fit"); + private parseRealToMonotonicTimeOffsetNs(videoData: Uint8Array, pos: number) : [number, bigint] { + if (pos + 8 > videoData.length) { + throw new TypeError("Failed to parse realtime-to-monotonic time offset. Video data is too short."); } - const timestamps: number[] = []; + const offset = ArrayUtils.toIntLittleEndian(videoData, pos, pos+8); + pos += 8; + return [pos, offset]; + } + + private parseFramesCount(videoData: Uint8Array, pos: number) : [number, number] { + if (pos + 4 > videoData.length) { + throw new TypeError("Failed to parse frames count. Video data is too short."); + } + const count = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+4)); + pos += 4; + return [pos, count]; + } + + private parseTimestampsMonotonicNs(videoData: Uint8Array, pos: number, count: number) : bigint[] { + if (pos + count * 16 > videoData.length) { + throw new TypeError("Failed to parse monotonic timestamps. Video data is too short."); + } + const timestamps: bigint[] = []; for (let i = 0; i < count; ++i) { - const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8) * 1000; + const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8); + pos += 8; + //parse VSYNC ID here when available pos += 8; timestamps.push(timestamp); } @@ -79,8 +113,7 @@ class ParserScreenRecording extends Parser { } private static readonly MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42 - private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!# - private static readonly EPSILON = 0.00001; + private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x32, 0x23]; // #VV1NSC0PET1ME2# } export {ParserScreenRecording}; diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.spec.ts b/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.spec.ts new file mode 100644 index 000000000..eff10b838 --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.spec.ts @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ScreenRecordingTraceEntry} from "common/trace/screen_recording"; +import {TraceTypeId} from "common/trace/type_id"; +import {TestUtils} from "test/test_utils"; +import {Parser} from "./parser"; +import {ParserFactory} from "./parser_factory"; + +describe("ParserScreenRecordingLegacy", () => { + let parser: Parser; + + beforeAll(async () => { + const trace = TestUtils.getFixtureBlob("screen_recording_legacy.mp4"); + const parsers = await new ParserFactory().createParsers([trace]); + expect(parsers.length).toEqual(1); + parser = parsers[0]; + }); + + it("has expected trace type", () => { + expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SCREEN_RECORDING); + }); + + it("provides timestamps", () => { + const timestamps = parser.getTimestamps(); + + expect(timestamps.length) + .toEqual(85); + + expect(timestamps.slice(0, 3)) + .toEqual([19446131807000, 19446158500000, 19446167117000]); + + expect(timestamps.slice(timestamps.length-3, timestamps.length)) + .toEqual([19448470076000, 19448487525000, 19448501007000]); + }); + + it("retrieves trace entry", () => { + { + const entry = parser.getTraceEntry(19446131807000)!; + expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry); + expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0); + } + + { + const entry = parser.getTraceEntry(19448501007000)!; + expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry); + expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001); + } + }); +}); diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts b/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts new file mode 100644 index 000000000..900016e12 --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {TraceTypeId} from "common/trace/type_id"; +import {ArrayUtils} from "common/utils/array_utils"; +import {Parser} from "./parser"; +import {ScreenRecordingTraceEntry} from "common/trace/screen_recording"; + +class ParserScreenRecordingLegacy extends Parser { + constructor(trace: Blob) { + super(trace); + } + + override getTraceTypeId(): TraceTypeId { + return TraceTypeId.SCREEN_RECORDING; + } + + override getMagicNumber(): number[] { + return ParserScreenRecordingLegacy.MPEG4_MAGIC_NMBER; + } + + override decodeTrace(videoData: Uint8Array): number[] { + const posCount = this.searchMagicString(videoData); + const [posTimestamps, count] = this.parseFramesCount(videoData, posCount); + return this.parseTimestamps(videoData, posTimestamps, count); + } + + override getTimestamp(decodedEntry: number): number { + return decodedEntry; + } + + override processDecodedEntry(timestamp: number): ScreenRecordingTraceEntry { + const videoTimeSeconds = (timestamp - this.timestamps[0]) / 1000000000 + ParserScreenRecordingLegacy.EPSILON; + const videoData = this.trace; + return new ScreenRecordingTraceEntry(timestamp, videoTimeSeconds, videoData); + } + + private searchMagicString(videoData: Uint8Array): number { + let pos = ArrayUtils.searchSubarray(videoData, ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING); + if (pos === undefined) { + throw new TypeError("video data doesn't contain winscope magic string"); + } + pos += ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING.length; + return pos; + } + + private parseFramesCount(videoData: Uint8Array, pos: number) : [number, number] { + if (pos + 4 > videoData.length) { + throw new TypeError("Failed to parse frames count. Video data is too short."); + } + const framesCount = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+4)); + pos += 4; + return [pos, framesCount]; + } + + private parseTimestamps(videoData: Uint8Array, pos: number, count: number): number[] { + if (pos + count * 8 > videoData.length) { + throw new TypeError("Failed to parse timestamps. Video data is too short."); + } + const timestamps: number[] = []; + for (let i = 0; i < count; ++i) { + const timestamp = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos+8) * 1000n); + pos += 8; + timestamps.push(timestamp); + } + return timestamps; + } + + private static readonly MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42 + private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!# + private static readonly EPSILON = 0.00001; +} + +export {ParserScreenRecordingLegacy}; diff --git a/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 b/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 index 9cdedfebe..e18879be4 100644 Binary files a/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 and b/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 differ diff --git a/tools/winscope-ng/src/test/fixtures/screen_recording_legacy.mp4 b/tools/winscope-ng/src/test/fixtures/screen_recording_legacy.mp4 new file mode 100644 index 000000000..9cdedfebe Binary files /dev/null and b/tools/winscope-ng/src/test/fixtures/screen_recording_legacy.mp4 differ