Skip to content

feat: Isolate MJPEG helpers in the driver - #1028

Merged
mykola-mokhnach merged 6 commits into
appium:masterfrom
mykola-mokhnach:mjpeg
Jul 27, 2026
Merged

feat: Isolate MJPEG helpers in the driver#1028
mykola-mokhnach merged 6 commits into
appium:masterfrom
mykola-mokhnach:mjpeg

Conversation

@mykola-mokhnach

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR moves MJPEG stream handling into this driver by introducing a local MJpegStream implementation (plus unit tests), and wiring the driver to use it instead of the upstream appium/support MJPEG helper.

Changes:

  • Added lib/utils/mjpeg.ts implementing MJPEG frame parsing and a MJpegStream class.
  • Updated the driver to instantiate the new local MJpegStream.
  • Added unit tests for MJpegStream behavior and added sharp as an optional dependency.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/unit/utils/mjpeg-specs.ts Adds unit coverage for MJPEG frame capture, conversion, and failure modes
package.json Adds sharp as an optional dependency for MJPEG PNG conversion support
lib/utils/mjpeg.ts Introduces the new MJPEG parser and stream wrapper used by the driver
lib/utils/index.ts Exposes MJpegStream from the utils barrel
lib/driver.ts Switches from appium/support MJPEG helper to the driver-local MJpegStream
Comments suppressed due to low confidence (1)

lib/utils/mjpeg.ts:236

  • If startPromise rejects (e.g., no frame arrives before the timeout), start() returns with responseStream still open and the piping still active, relying on callers to remember to stop(). This can leak an open HTTP connection and keep the process busy even though start() failed.
      this.registerStartFailure = reject;
      timeoutId = setTimeout(
        () =>
          reject(new Error(`Waited ${serverTimeout}ms but the MJPEG server never sent any images`)),
        serverTimeout,
      );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/utils/mjpeg.ts
Comment thread lib/utils/mjpeg.ts
Comment on lines +210 to +213
const onClose = () => {
log.debug(`The connection to MJPEG server at ${url} has been closed`);
this.lastChunk = null;
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 62bda99. onClose() now rejects the pending registerStartFailure (a no-op if start() already resolved), so a connection that closes before any frame arrives fails immediately instead of waiting out the full timeout. Also addressed the related "suppressed due to low confidence" comment about leaking the connection: start() now calls this.stop() in a catch before rethrowing, so pipes/the response stream are torn down on any failure path (timeout or early close). Added tests covering both the fast-rejection timing and that responseStream is cleared after a failed start().

Comment thread package.json
mykola-mokhnach and others added 2 commits July 24, 2026 10:18
Fixes two issues flagged on PR appium#1028:
- MjpegFrameParser.emitFrame() pushed the full pre-allocated buffer
  even when fewer bytes were actually copied, leaking trailing zero
  bytes, and never cleared its frame state, so a later stray chunk
  could be appended onto an already-emitted, downstream buffer.
- MJpegStream.start() waited for the full timeout instead of failing
  fast when the underlying connection closed before any frame arrived,
  and left the connection/pipes open on failure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread lib/utils/mjpeg.ts Outdated
…ser.appendChunk()

appendChunk() continues a frame that is already open, so the whole chunk
belongs to it and must be copied from offset 0. Any SOI marker found in
that chunk necessarily belongs to the *next* frame (it can only appear
after the current frame's own EOI), so using it as the copy start skipped
the current frame's tail bytes and could truncate it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mykola-mokhnach
mykola-mokhnach merged commit f32c5b7 into appium:master Jul 27, 2026
24 of 25 checks passed
@mykola-mokhnach
mykola-mokhnach deleted the mjpeg branch July 27, 2026 16:55
github-actions Bot pushed a commit that referenced this pull request Jul 27, 2026
## [8.2.0](v8.1.2...v8.2.0) (2026-07-27)

### Features

* Isolate MJPEG helpers in the driver ([#1028](#1028)) ([f32c5b7](f32c5b7))
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 8.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants