npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@9b9387/android-stream-scrcpy

v0.1.2

Published

Versioned scrcpy protocol client for Node.js/Electron

Downloads

80

Readme

@9b9387/android-stream-scrcpy

TypeScript scrcpy client library for Node.js and Electron. It starts scrcpy-server through @devicefarmer/adbkit, reads the video/audio/control sockets, and exposes media packets as Web Standards binary types (Uint8Array).

The bundled protocol implementation targets scrcpy 4.0. Protocol code is versioned so future versions, such as 4.1, can be added side by side without rewriting the backend or service layers.

Runtime note: the root package is Node-only. Use it from a Node.js process, an Electron main process, a Next.js Node runtime route/server, or another long-lived backend process. Do not import the root package from browser code, Next.js Client Components, or Edge Runtime handlers.

Project Structure

node-lib/
  assets/
    scrcpy-server-v4.0.jar
  examples/
    demo.ts
    server.ts
  src/
    backend/
      adb/
        scrcpy-adb-client.ts
      io/
        buffered-stream-reader.ts
      server/
        options.ts
        server-command.ts
        socket-name.ts
      scrcpy-backend.ts
      index.ts
    protocol/
      core/
        binary.ts
        types.ts
      registry.ts
      v4_0/
        codecs.ts
        control-message.ts
        control-message-types.ts
        device-message.ts
        frame-header.ts
        server-options.ts
        string-payload.ts
        index.ts
      index.ts
    service/
      packet-queue-subscriber.ts
      scrcpy-stream-service.ts
      types.ts
      index.ts
    snapshot/
      ffmpeg-snapshot-cache.ts
      types.ts
      index.ts
    websocket/
      binary-packet.ts
      control-json.ts
      scrcpy-websocket-bridge.ts
      types.ts
      index.ts
    index.ts

Module Responsibilities

  • src/protocol/: scrcpy wire protocol implementation. core/ contains shared binary helpers and protocol interfaces; registry.ts selects a protocol adapter by version; v4_0/ contains scrcpy 4.0 frame, control, device, codec, and server-option logic.
  • src/backend/: adbkit-only device integration. adb/ wraps adbkit operations, io/ contains socket reading utilities, server/ builds and normalizes scrcpy server launch details, and scrcpy-backend.ts orchestrates the streaming connection.
  • src/service/: public stream service. It manages state, caches decoder config/session snapshots, and exposes for await...of media packet subscriptions.
  • src/snapshot/: optional Node-side screenshot cache. It pipes H.264 packets into ffmpeg and stores the latest JPEG frame.
  • src/websocket/: optional ws bridge for browser clients. It is exported from the ./websocket subpath and is not part of the root API.
  • examples/: runnable examples kept outside the library build.

Install

Install the package in your application:

npm install @9b9387/android-stream-scrcpy

Core runtime dependency is @devicefarmer/adbkit. The library does not call the adb system command directly.

If your application uses the optional WebSocket bridge, install ws in the application:

npm install ws

If your application uses the optional screenshot cache, make sure ffmpeg is available on PATH, or pass ffmpegPath to FfmpegSnapshotCache.

Build From Source

cd node-lib
npm install
npm run build

Package Entrypoints

  • @9b9387/android-stream-scrcpy: Node-only service/backend API.
  • @9b9387/android-stream-scrcpy/protocol: protocol adapters and types. This is the safest entrypoint for code that only needs scrcpy protocol constants, codecs, and message types.
  • @9b9387/android-stream-scrcpy/websocket: optional Node-only ws bridge.
  • @9b9387/android-stream-scrcpy/snapshot: optional Node-only ffmpeg screenshot cache.

The package is ESM-only and requires Node.js 20 or newer.

Run Examples

Run the console stream demo:

npm run example:demo

Run the browser demo server:

npm run example:server

The server serves the existing demo page from the repository root:

../web_demo/scrcpy.html

Then open:

http://localhost:8000

You can also request the page directly:

http://localhost:8000/scrcpy.html

Choose a specific connected Android device by serial:

ANDROID_SERIAL=<device-serial> npm run example:server

ADB_SERIAL is also supported for compatibility with existing local workflows.

Enable the optional Node-side screenshot cache for the browser demo:

SNAPSHOT_ENABLED=1 SNAPSHOT_FPS=2 SNAPSHOT_JPEG_QUALITY=85 npm run example:server

This requires ffmpeg on PATH by default. Set FFMPEG_PATH=/path/to/ffmpeg if the binary lives elsewhere. When enabled, the demo exposes:

http://localhost:8000/screenshot.jpg

The scrcpy.html page also shows a 截图 button that downloads the latest cached JPEG. The cache is produced from the scrcpy H.264 stream in Node; it does not call ADB screencap and does not require a browser canvas.

Integrate The Core Library

import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";

const service = new ScrcpyStreamService({
  protocolVersion: "4.0",
  maxSize: 1080,
  video: true,
  audio: true,
  control: true,
});

const meta = await service.start();
console.log(`Connected to ${meta.deviceName}`);

for await (const packet of service.subscribe()) {
  // packet.kind: "video" | "audio" | "session"
  // packet.payload: Uint8Array
  if (packet.kind === "video") {
    // Feed H.264/H.265/AV1 bytes into your decoder.
  }
}

Stop streaming when your application shuts down:

service.stop();

Send Control Messages

import {
  ControlMessageType,
  KEY_ACTION_DOWN,
  ScrcpyStreamService,
} from "@9b9387/android-stream-scrcpy";

const service = new ScrcpyStreamService();
await service.start();

service.sendControlMessage({
  type: ControlMessageType.INJECT_KEYCODE,
  action: KEY_ACTION_DOWN,
  keycode: 26,
});

The scrcpy 4.0 adapter includes key, text, touch, scroll, clipboard, UHID, app start, display power, camera, and display resize control messages.

Integrate The WebSocket Bridge

The WebSocket bridge is optional and imported from a subpath:

import { createServer } from "node:http";
import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";
import { ScrcpyWebSocketBridge } from "@9b9387/android-stream-scrcpy/websocket";

const service = new ScrcpyStreamService({ protocolVersion: "4.0" });
const server = createServer();
const bridge = new ScrcpyWebSocketBridge(service, { server });

await service.start();
server.listen(8000);

process.on("SIGINT", () => {
  bridge.close();
  service.stop();
  server.close();
});

The bridge sends an initial JSON init message, then binary media packets with a 16-byte header followed by the media payload.

Integrate The Screenshot Cache

The screenshot cache is optional and imported from the ./snapshot subpath:

import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";
import { FfmpegSnapshotCache } from "@9b9387/android-stream-scrcpy/snapshot";

const service = new ScrcpyStreamService({ videoCodec: "h264" });
const snapshots = new FfmpegSnapshotCache(service, {
  enabled: true,
  fps: 2,
  quality: 85,
  // Optional robustness knobs (defaults shown):
  drainTimeoutMs: 5000, // tear down ffmpeg if stdin stalls this long
  killTimeoutMs: 2000, // SIGTERM grace before SIGKILL on stop()
  staleTimeoutMs: 10000, // emit "stale" when no new frame for this long (0 = off)
  maxStdoutBytes: 16 * 1024 * 1024, // cap on the JPEG reassembly buffer
});

await service.start();
snapshots.start(); // must be called after service.start()

// Non-blocking read of the most recent frame (may be null before the first):
const latest = snapshots.latest();

// Or wait up to N ms for the first/next frame instead of busy-polling 404s:
try {
  const shot = await snapshots.waitForFresh(3000);
  // shot.contentType === "image/jpeg"; shot.data is a JPEG Buffer
} catch {
  // no frame within the timeout — surface a 503 / "warming up" to the client
}

// Observe a stalled stream (device asleep, encoder paused, etc.):
snapshots.on("stale", ({ ageMs }) => {
  console.warn(`no new snapshot for ${ageMs}ms`);
});

The first implementation supports H.264 input and JPEG output. The configured fps limits how often ffmpeg emits JPEG frames; ffmpeg still receives the continuous H.264 stream so inter-frame decoding remains correct.

Lifecycle & resource safety

  • FfmpegSnapshotCache listens to the service state: when the service stops or errors, the ffmpeg process is killed automatically, so it never lingers as an orphan across reconnects. You should still call snapshots.stop() explicitly when you tear a session down yourself.
  • waitForFresh() resolves immediately if a frame is cached, otherwise it resolves on the next frame or rejects after the timeout — use it in HTTP handlers so a single request never hangs and clients never busy-poll.
  • The WebSocket bridge applies backpressure: a slow client's droppable video frames are dropped (config/keyframe/session packets are preserved) once its outbound buffer exceeds maxBufferedBytes (8MiB default), preventing unbounded memory growth. Tune via new ScrcpyWebSocketBridge(service, { maxBufferedBytes }).

For multi-device streaming and per-device screenshot HTTP routes, see docs/multi-device-screenshot.md (Web integration guide, 中文).

Next.js And Electron Usage

Next.js

Use this package only from the server side of a self-hosted or long-lived Node.js runtime:

export const runtime = "nodejs";

import { ScrcpyStreamService } from "@9b9387/android-stream-scrcpy";

Do not import the root package from Client Components, browser bundles, Middleware, or Edge Runtime route handlers. The backend opens ADB sockets and long-lived media streams, so a custom Node.js server or separate local daemon is usually a better fit than a short-lived serverless function.

Electron

Create and manage ScrcpyStreamService in the main process. Renderer processes should communicate with the main process through IPC or connect to the optional WebSocket bridge.

When packaging Electron apps, make sure assets/scrcpy-server-v4.0.jar is copied as a runtime resource. If your packager moves or packs assets into an archive, pass an explicit serverJarPath to ScrcpyStreamService. If screenshot caching is enabled, also ship ffmpeg yourself or pass ffmpegPath.

Protocol Versioning

protocolVersion defaults to "4.0":

const service = new ScrcpyStreamService({ protocolVersion: "4.0" });

To add a future scrcpy version:

  1. Add a new adapter under src/protocol/.
  2. Register it in src/protocol/registry.ts.
  3. Add the matching scrcpy-server jar to assets/.
  4. Add fixed-byte protocol tests for changed behavior.

The backend depends on the protocol interface, so frame parsing, control serialization, server options, and socket naming can evolve per version.

Development Commands

npm run format
npm run lint
npm run typecheck
npm test
npm run build
npm pack --dry-run

End-to-end streaming requires a connected Android device. Unit tests cover protocol serialization/parsing, backend option normalization, subscriber queue behavior, and WebSocket packet encoding.

Publish

Before publishing, make sure you are logged in to the npm account that owns the @9b9387 scope:

npm whoami

Run the release checks and inspect the tarball contents:

npm run typecheck
npm run test
npm run lint
npm run build
npm pack --dry-run

Publish the public scoped package:

npm publish --access public