• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {ArrayUtils} from 'common/array_utils';
18import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
19import {Timestamp, TimestampType} from 'trace/timestamp';
20import {TraceFile} from 'trace/trace_file';
21import {TraceType} from 'trace/trace_type';
22import {AbstractParser} from './abstract_parser';
23
24class ParserScreenRecordingLegacy extends AbstractParser {
25  constructor(trace: TraceFile) {
26    super(trace);
27  }
28
29  override getTraceType(): TraceType {
30    return TraceType.SCREEN_RECORDING;
31  }
32
33  override getMagicNumber(): number[] {
34    return ParserScreenRecordingLegacy.MPEG4_MAGIC_NMBER;
35  }
36
37  override decodeTrace(videoData: Uint8Array): Timestamp[] {
38    const posCount = this.searchMagicString(videoData);
39    const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
40    return this.parseTimestamps(videoData, posTimestamps, count);
41  }
42
43  override getTimestamp(type: TimestampType, decodedEntry: Timestamp): undefined | Timestamp {
44    if (type !== TimestampType.ELAPSED) {
45      return undefined;
46    }
47    return decodedEntry;
48  }
49
50  override processDecodedEntry(
51    index: number,
52    timestampType: TimestampType,
53    entry: Timestamp
54  ): ScreenRecordingTraceEntry {
55    const currentTimestamp = entry;
56    const initialTimestamp = this.getTimestamps(TimestampType.ELAPSED)![0];
57    const videoTimeSeconds =
58      Number(currentTimestamp.getValueNs() - initialTimestamp.getValueNs()) / 1000000000 +
59      ParserScreenRecordingLegacy.EPSILON;
60    const videoData = this.traceFile.file;
61    return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
62  }
63
64  private searchMagicString(videoData: Uint8Array): number {
65    let pos = ArrayUtils.searchSubarray(
66      videoData,
67      ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING
68    );
69    if (pos === undefined) {
70      throw new TypeError("video data doesn't contain winscope magic string");
71    }
72    pos += ParserScreenRecordingLegacy.WINSCOPE_META_MAGIC_STRING.length;
73    return pos;
74  }
75
76  private parseFramesCount(videoData: Uint8Array, pos: number): [number, number] {
77    if (pos + 4 > videoData.length) {
78      throw new TypeError('Failed to parse frames count. Video data is too short.');
79    }
80    const framesCount = Number(ArrayUtils.toUintLittleEndian(videoData, pos, pos + 4));
81    pos += 4;
82    return [pos, framesCount];
83  }
84
85  private parseTimestamps(videoData: Uint8Array, pos: number, count: number): Timestamp[] {
86    if (pos + count * 8 > videoData.length) {
87      throw new TypeError('Failed to parse timestamps. Video data is too short.');
88    }
89    const timestamps: Timestamp[] = [];
90    for (let i = 0; i < count; ++i) {
91      const value = ArrayUtils.toUintLittleEndian(videoData, pos, pos + 8) * 1000n;
92      pos += 8;
93      timestamps.push(new Timestamp(TimestampType.ELAPSED, value));
94    }
95    return timestamps;
96  }
97
98  private static readonly MPEG4_MAGIC_NMBER = [
99    0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32,
100  ]; // ....ftypmp42
101  private static readonly WINSCOPE_META_MAGIC_STRING = [
102    0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23,
103  ]; // #VV1NSC0PET1ME!#
104  private static readonly EPSILON = 0.00001;
105}
106
107export {ParserScreenRecordingLegacy};
108