Skip to content
This repository has been archived by the owner on Oct 25, 2023. It is now read-only.

add mjpeg stream helper class to basedriver #230

Closed
wants to merge 1 commit into from
Closed

Conversation

jlipps
Copy link
Member

@jlipps jlipps commented Jul 5, 2018

There is this "standard" of sorts called MJPEG, which allows for streaming of images over HTTP. Some users of Appium have MJPEG servers connected to the devices they use with Appium, which deliver relatively high-framerate images off the device. If this is the case, why not just use it for screenshots as well and bypass the sometimes time-consuming Appium-internal screenshot commands?

MJPEG is a sort-of "push" mechanism, so in this PR I just provide a class which maintains a reference to the last-pushed image, which would then be available to any driver that wants it. I'll start the driver PRs soon, but the basic idea is that this will be behind a capability, which if turned on will just send back the last-received image immediately upon screenshot request. Something like this:

// in driver session start
if (this.opts.mjpegScreenshotUrl) {
  this.mjpegStream = new MJpegStream(this.opts.mjpegScreenshotUrl);
  this.mjpegStream.start();
}
// in screenshot command implementation 
if (this.mjpegStream && this.mjpegStream.lastChunkBase64) {
  return this.mjpegStream.lastChunkBase64;
}

// now continue with regular screenshot logic as a fallback
// in driver session stop/cleanup
if (this.mjpegStream) {
  this.mjpegStream.stop();
}

* @returns {string}
*/
get lastChunkBase64 () {
if (!this.lastChunk) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ternary operator might be shorter here

/**
* Start reading the MJpeg stream and storing the last image
*/
start () {
Copy link
Contributor

Choose a reason for hiding this comment

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

why not async?

// just send the same jpeg over and over
for (let i = 0; i < times; i++) {
await B.delay(intMs);
mJpegReqHandler._write(jpg, null, () => {});
Copy link
Contributor

Choose a reason for hiding this comment

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

there is also _.noop helper

@mykola-mokhnach
Copy link
Contributor

We probably would need some fast PNG->JPEG converter here as well

@jlipps
Copy link
Member Author

jlipps commented Jul 5, 2018

We probably would need some fast PNG->JPEG converter here as well

For now my thought is if someone uses this, they know they will get jpeg formatted screenshots out of appium instead of png.

@mykola-mokhnach
Copy link
Contributor

For now my thought is if someone uses this, they know they will get jpeg formatted screenshots out of appium instead of png.

Selenium's getScreenshot always returns images in PNG format. There are no other endpoints, which allow to get JPEG directly. That is why I suppose we need to do the conversion on Appium side

* @returns {string}
*/
get lastChunkBase64 () {
return this.lastChunk ? this.lastChunk.toString('base64') : null;
Copy link
Contributor

Choose a reason for hiding this comment

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

_.isBuffer(lastChunk) ?

/**
* Start reading the MJpeg stream and storing the last image
*/
async start () {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

* the HTTP request itself. Then reset the state.
*/
stop () {
if (this.consumer) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if (!this.consumer) return

.pipe(this.consumer) // allow chunking and transforming of jpeg data
.pipe(this); // send the actual jpegs to ourself

await startPromise;
Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Jul 5, 2018

Choose a reason for hiding this comment

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

should it block till the first image is received?

Copy link
Member Author

Choose a reason for hiding this comment

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

there is no last image, so that's not possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean this might block forever if the server does not produce any data

Copy link
Member Author

Choose a reason for hiding this comment

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

oh right. that would be an error. probably we should have a timeout and if the timeout gets hit, throw an error

* Reset internal state
*/
clear () {
this.didStart = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

is this used?

Copy link
Member Author

Choose a reason for hiding this comment

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

nope! will remove

@jlipps
Copy link
Member Author

jlipps commented Jul 5, 2018

@mykola-mokhnach @imurchie what do you think, should this be a part of appium-support instead? now that it is just a class which can be used, but not tied to the driver code itself.

@mykola-mokhnach
Copy link
Contributor

If the class has no direct dependencies on base driver then yes, it would be better to keep it in appium-support


// start a timeout so that if the server does not return data, we don't
// block forever.
setTimeout(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think bluebird promises already have the standard .timeout method

@appium appium deleted a comment from jlipps Jul 5, 2018
this.lastChunk.toString('base64') :
null;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

extra empty line here

@jlipps
Copy link
Member Author

jlipps commented Jul 5, 2018

closing, gonna move to appium-support

@jlipps jlipps closed this Jul 5, 2018
@jlipps jlipps deleted the jlipps-mjpeg branch July 5, 2018 19:15
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants