Skip to content

Issue with delay, trying to stream to a client in realtime #177

@stoefln

Description

@stoefln

I am trying to create a close-to-realtime streaming implementation. The user should be able to remotely control the browser, so the streaming delay needs to be minimal. I tried different solutions, the most straight forward one is the following, but for some reason the video does not start playing, even though a lot of data has been streamed already.

Screenshot 2024-11-17 at 09 30 04

Here is my (next.js) client code:

import React, { useState, useEffect, useRef } from 'react'

const VideoTestPage = () => {
  const videoRef = useRef(null)

  return (
    <div>
      <video
        ref={videoRef}
        src="/api/browsers/direct-video-test"
        controls
        autoPlay={true}
        style={{ width: '100%', height: 'auto' }}
        preload="none"
        muted // Ensure autoplay works
      ></video>
    </div>
  )
}

export default VideoTestPage

And here the API endpoint code:

import { getStream, launch } from 'puppeteer-stream'

let browsers = {}

const startStreamingBrowser = async (userId, size, url) => {
  const { width = 800, height = 600 } = size

  let stream = null
  try {
    let browser = browsers[userId]
    let page
    if (!browser) {
      console.log('start new browser')
      browser = await launch({
        //executablePath: utils.getExecutablePath(),
        executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
        defaultViewport: {
          width: parseInt(width, 10),
          height: parseInt(height, 10)
        }
      })

      console.log('create new page')
      page = await browser.newPage()
      browser.page = page
      browsers[userId] = browser
    } else {
      page = browser.page
    }

    console.log('go to url: ', url)
    await page.goto(url)

    stream = await getStream(page, { audio: true, video: true, frameSize: 200 })

    stream.on('close', async () => {
      await browser.close()
      browsers[userId] = null
    })

    stream.on('error', async error => {
      console.error('Error occurred:', error)
      await browser.close()
      browsers[userId] = null
    })

    stream.on('end', async () => {
      console.log('Stream end -> closing browser instance')
      try {
        await browser.close()
      } catch (error) {
        console.log('Closing browser failed. Maybe browser was closed already manually...')
      }
      browsers[userId] = null
    })
  } catch (error) {
    console.error('Error occurred:', error)
  }

  return stream
}

const stopStreamingBrowser = async userId => {
  const browser = browsers[userId]
  if (browser) {
    await browser.close()
    browsers[userId] = null
  }
}

export default async function handler(req, res) {
  console.log('browser endpoint', req.method)
  const userId = 'test'
  const stream = await startStreamingBrowser(
    userId,
    { width: 800, height: 600 },
    'https://giphy.com/clips/studiosoriginals-happy-birthday-grandson-Ny5rE4B0R6i1vqOOaj'
  )

  // Stream headers
  res.setHeader('Content-Type', 'video/mp4'); 
  res.setHeader('Transfer-Encoding', 'chunked');
  res.setHeader('Connection', 'keep-alive');
  
  stream.pipe(res)
}

export const config = {
  api: {
    responseLimit: false,
  },
}


Any ideas how I could get this working?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions