Loading libraries/scrcpy/src/options/1_16/h264-configuration.ts +191 −155 Original line number Diff line number Diff line // cspell: ignore golomb // cspell: ignore qpprime // H.264 has two standards: ITU-T H.264 and ISO/IEC 14496-10 // they have the same content, and refer themselves as "H.264". // The name "AVC" (Advanced Video Coding) is only used in ISO spec name, // and other ISO specs referring to H.264. // Because this module parses H.264 Annex B format, // it's named "h264" instead of "avc". class BitReader { private buffer: Uint8Array; Loading Loading @@ -43,6 +50,9 @@ class BitReader { } } /** * Parse NAL units from H.264 Annex B formatted data. */ function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> { // -1 means we haven't found the first start code let start = -1; Loading Loading @@ -126,7 +136,7 @@ function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> { inEmulation = true; break; default: // `0x000004` or larger are ok // `0x000004` or larger are as-is break; } } Loading @@ -139,9 +149,9 @@ function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> { } // 7.3.2.1.1 Sequence parameter set data syntax export function parse_sequence_parameter_set(buffer: ArrayBuffer) { for (const nalu of iterateNalu(new Uint8Array(buffer))) { const reader = new BitReader(nalu); // Variable names in this method uses the snake_case convention as in the spec for easier referencing. export function parseSequenceParameterSet(buffer: Uint8Array) { const reader = new BitReader(buffer); if (reader.next() !== 0) { throw new Error("Invalid data"); } Loading @@ -150,7 +160,7 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { const nal_unit_type = reader.read(5); if (nal_unit_type !== 7) { continue; throw new Error("Invalid data"); } if (nal_ref_idc === 0) { Loading Loading @@ -209,11 +219,7 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { const seq_scaling_matrix_present_flag = !!reader.next(); if (seq_scaling_matrix_present_flag) { const seq_scaling_list_present_flag: boolean[] = []; for ( let i = 0; i < (chroma_format_idc !== 3 ? 8 : 12); i += 1 ) { for (let i = 0; i < (chroma_format_idc !== 3 ? 8 : 12); i += 1) { seq_scaling_list_present_flag[i] = !!reader.next(); if (seq_scaling_list_present_flag[i]) if (i < 6) { Loading Loading @@ -246,8 +252,7 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { reader.decodeExponentialGolombNumber(); const offset_for_ref_frame: number[] = []; for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i += 1) { offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber(); offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber(); } } Loading Loading @@ -313,9 +318,40 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { }; } throw new Error("Invalid data"); /** * Find Sequence Parameter Set (SPS) and Picture Parameter Set (PPS) * from H.264 Annex B formatted data. */ export function parseH264Configuration(buffer: Uint8Array) { let sequenceParameterSet: Uint8Array | undefined; let pictureParameterSet: Uint8Array | undefined; for (const nalu of iterateNalu(buffer)) { const naluType = nalu[0]! & 0x1f; switch (naluType) { case 7: // Sequence parameter set sequenceParameterSet = nalu; if (pictureParameterSet) { return { sequenceParameterSet, pictureParameterSet, }; } break; case 8: // Picture parameter set pictureParameterSet = nalu; if (sequenceParameterSet) { return { sequenceParameterSet, pictureParameterSet, }; } break; default: // ignore break; } } export type SequenceParameterSet = ReturnType< typeof parse_sequence_parameter_set >; throw new Error("Invalid data"); } libraries/scrcpy/src/options/1_16/index.ts +3 −3 Original line number Diff line number Diff line export * from './codec-options.js'; export * from './options.js'; export * from "./codec-options.js"; export * from "./h264-configuration.js"; export * from "./options.js"; export * from "./scroll.js"; export * from './sps.js'; libraries/scrcpy/src/options/1_16/options.ts +14 −8 Original line number Diff line number Diff line Loading @@ -16,11 +16,14 @@ import { } from "../types.js"; import { CodecOptions } from "./codec-options.js"; import { parseH264Configuration, parseSequenceParameterSet, } from "./h264-configuration.js"; import { ScrcpyScrollController1_16, type ScrcpyScrollController, } from "./scroll.js"; import { parse_sequence_parameter_set } from "./sps.js"; export enum ScrcpyLogLevel { Verbose = "verbose", Loading Loading @@ -109,8 +112,9 @@ export const ScrcpyBackOrScreenOnControlMessage1_16 = new Struct().uint8( ScrcpyControlMessageType.BackOrScreenOn as const ); export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16> implements ScrcpyOptions<T> export class ScrcpyOptions1_16< T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16 > implements ScrcpyOptions<T> { public value: Partial<T>; Loading Loading @@ -206,10 +210,10 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn new TransformStream({ transform(packet, controller) { if (packet.pts === NO_PTS) { const sequenceParameterSet = parse_sequence_parameter_set( packet.data.slice().buffer ); const { sequenceParameterSet, pictureParameterSet, } = parseH264Configuration(packet.data.slice()); const { profile_idc: profileIndex, Loading @@ -222,7 +226,7 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset, } = sequenceParameterSet; } = parseSequenceParameterSet(sequenceParameterSet); const encodedWidth = (pic_width_in_mbs_minus1 + 1) * 16; Loading @@ -243,6 +247,8 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn header = packet.data; controller.enqueue({ type: "configuration", pictureParameterSet, sequenceParameterSet, data: { profileIndex, constraintSet, Loading libraries/scrcpy/src/options/types.ts +2 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,8 @@ export interface H264Configuration { export interface ScrcpyVideoStreamConfigurationPacket { type: "configuration"; sequenceParameterSet: Uint8Array; pictureParameterSet: Uint8Array; data: H264Configuration; } Loading Loading
libraries/scrcpy/src/options/1_16/h264-configuration.ts +191 −155 Original line number Diff line number Diff line // cspell: ignore golomb // cspell: ignore qpprime // H.264 has two standards: ITU-T H.264 and ISO/IEC 14496-10 // they have the same content, and refer themselves as "H.264". // The name "AVC" (Advanced Video Coding) is only used in ISO spec name, // and other ISO specs referring to H.264. // Because this module parses H.264 Annex B format, // it's named "h264" instead of "avc". class BitReader { private buffer: Uint8Array; Loading Loading @@ -43,6 +50,9 @@ class BitReader { } } /** * Parse NAL units from H.264 Annex B formatted data. */ function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> { // -1 means we haven't found the first start code let start = -1; Loading Loading @@ -126,7 +136,7 @@ function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> { inEmulation = true; break; default: // `0x000004` or larger are ok // `0x000004` or larger are as-is break; } } Loading @@ -139,9 +149,9 @@ function* iterateNalu(buffer: Uint8Array): Generator<Uint8Array> { } // 7.3.2.1.1 Sequence parameter set data syntax export function parse_sequence_parameter_set(buffer: ArrayBuffer) { for (const nalu of iterateNalu(new Uint8Array(buffer))) { const reader = new BitReader(nalu); // Variable names in this method uses the snake_case convention as in the spec for easier referencing. export function parseSequenceParameterSet(buffer: Uint8Array) { const reader = new BitReader(buffer); if (reader.next() !== 0) { throw new Error("Invalid data"); } Loading @@ -150,7 +160,7 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { const nal_unit_type = reader.read(5); if (nal_unit_type !== 7) { continue; throw new Error("Invalid data"); } if (nal_ref_idc === 0) { Loading Loading @@ -209,11 +219,7 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { const seq_scaling_matrix_present_flag = !!reader.next(); if (seq_scaling_matrix_present_flag) { const seq_scaling_list_present_flag: boolean[] = []; for ( let i = 0; i < (chroma_format_idc !== 3 ? 8 : 12); i += 1 ) { for (let i = 0; i < (chroma_format_idc !== 3 ? 8 : 12); i += 1) { seq_scaling_list_present_flag[i] = !!reader.next(); if (seq_scaling_list_present_flag[i]) if (i < 6) { Loading Loading @@ -246,8 +252,7 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { reader.decodeExponentialGolombNumber(); const offset_for_ref_frame: number[] = []; for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i += 1) { offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber(); offset_for_ref_frame[i] = reader.decodeExponentialGolombNumber(); } } Loading Loading @@ -313,9 +318,40 @@ export function parse_sequence_parameter_set(buffer: ArrayBuffer) { }; } throw new Error("Invalid data"); /** * Find Sequence Parameter Set (SPS) and Picture Parameter Set (PPS) * from H.264 Annex B formatted data. */ export function parseH264Configuration(buffer: Uint8Array) { let sequenceParameterSet: Uint8Array | undefined; let pictureParameterSet: Uint8Array | undefined; for (const nalu of iterateNalu(buffer)) { const naluType = nalu[0]! & 0x1f; switch (naluType) { case 7: // Sequence parameter set sequenceParameterSet = nalu; if (pictureParameterSet) { return { sequenceParameterSet, pictureParameterSet, }; } break; case 8: // Picture parameter set pictureParameterSet = nalu; if (sequenceParameterSet) { return { sequenceParameterSet, pictureParameterSet, }; } break; default: // ignore break; } } export type SequenceParameterSet = ReturnType< typeof parse_sequence_parameter_set >; throw new Error("Invalid data"); }
libraries/scrcpy/src/options/1_16/index.ts +3 −3 Original line number Diff line number Diff line export * from './codec-options.js'; export * from './options.js'; export * from "./codec-options.js"; export * from "./h264-configuration.js"; export * from "./options.js"; export * from "./scroll.js"; export * from './sps.js';
libraries/scrcpy/src/options/1_16/options.ts +14 −8 Original line number Diff line number Diff line Loading @@ -16,11 +16,14 @@ import { } from "../types.js"; import { CodecOptions } from "./codec-options.js"; import { parseH264Configuration, parseSequenceParameterSet, } from "./h264-configuration.js"; import { ScrcpyScrollController1_16, type ScrcpyScrollController, } from "./scroll.js"; import { parse_sequence_parameter_set } from "./sps.js"; export enum ScrcpyLogLevel { Verbose = "verbose", Loading Loading @@ -109,8 +112,9 @@ export const ScrcpyBackOrScreenOnControlMessage1_16 = new Struct().uint8( ScrcpyControlMessageType.BackOrScreenOn as const ); export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16> implements ScrcpyOptions<T> export class ScrcpyOptions1_16< T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16 > implements ScrcpyOptions<T> { public value: Partial<T>; Loading Loading @@ -206,10 +210,10 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn new TransformStream({ transform(packet, controller) { if (packet.pts === NO_PTS) { const sequenceParameterSet = parse_sequence_parameter_set( packet.data.slice().buffer ); const { sequenceParameterSet, pictureParameterSet, } = parseH264Configuration(packet.data.slice()); const { profile_idc: profileIndex, Loading @@ -222,7 +226,7 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset, } = sequenceParameterSet; } = parseSequenceParameterSet(sequenceParameterSet); const encodedWidth = (pic_width_in_mbs_minus1 + 1) * 16; Loading @@ -243,6 +247,8 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn header = packet.data; controller.enqueue({ type: "configuration", pictureParameterSet, sequenceParameterSet, data: { profileIndex, constraintSet, Loading
libraries/scrcpy/src/options/types.ts +2 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,8 @@ export interface H264Configuration { export interface ScrcpyVideoStreamConfigurationPacket { type: "configuration"; sequenceParameterSet: Uint8Array; pictureParameterSet: Uint8Array; data: H264Configuration; } Loading