WebTransport server does not support live-streaming

253 views
Skip to first unread message

guest271314

unread,
Nov 21, 2021, 10:33:46 AM11/21/21
to web-transport-dev

WebTransport server does not support live-streaming (media streams; data streams).

Steps to reproduce: Modify webtransport_server.py (https://github.com/GoogleChrome/samples/blob/gh-pages/webtransport/webtransport_server.py) to stream content using, for example

for c in iter(lambda: ''.join(choice(digits) for i in range(512)).encode('ascii'), b''): # replace '' with b'' for Python 3 if c is not None: self._http._quic.send_stream_data( event.stream_id, c, end_stream=False)

and

cmd = 'parec', '-d', 'alsa_output.pci-0000_00_1.analog-stereo.monitor' process = subprocess.Popen(cmd, stdout=subprocess.PIPE) os.set_blocking(process.stdout.fileno(), False) for c in iter(lambda: process.stdout.read(512), b''): if c is not None: self._http._quic.send_stream_data( event.stream_id, c, end_stream=False)

Expected result: Data to be streamed to WebTransport client.
Actual result: No data is streamed to client.

guest271314

unread,
Nov 21, 2021, 1:04:13 PM11/21/21
to web-transport-dev, guest271314
Before the browser crashes I will post what I am doing this, which eventually results in 

    Connection failure: Connection terminated

at the server

    def h3_event_received(self, event: H3Event) -> None:
        if isinstance(event, DatagramReceived):
            # payload = str(len(event.data)).encode('ascii')
            # self._http.send_datagram(self._session_id, payload)
            def to_infinity():
                index = 0
                cmd = 'parec', '-d', 'alsa_output.pci-0000_00_1.analog-stereo.monitor'
                process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
                # process = subprocess.Popen(split(receivedMessage), stdout=subprocess.PIPE)
                os.set_blocking(process.stdout.fileno(), False)
                for chunk in iter(lambda: process.stdout.read(512), b''):
                    if chunk is not None:
                       # encoded = str([int('%02X' % i, 16) for i in chunk])
                        yield [index, chunk]
                        index += 1

            for [i, encoded] in to_infinity():
                print(i)
                self._http.send_datagram(self._session_id, encoded) 
                if i == 100:
                    break


// Reads datagrams from |transport| into the event log until EOF is reached.
async function readDatagrams(transport) {
  try {
    var reader = transport.datagrams.readable.getReader();
    addToEventLog('Datagram reader ready.');
  } catch (e) {
    addToEventLog('Receiving datagrams not supported: ' + e, 'error');
    return;
  }
  // let decoder = new TextDecoder('utf-8');
  let n = 0;
  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) {
        addToEventLog('Done reading datagrams!');
        return;
      }
      // let data = decoder.decode(value);
      console.log(++n, value);
      if (n === 100) {
        n = 0;
        await currentTransportDatagramWriter.write(new Uint8Array([1]));
      }
// addToEventLog('Datagram received: ');
    }
  } catch (e) {
    addToEventLog('Error while reading datagrams: ' + e, 'error');
  }
}

There has to be a canonical, or better way to do this.


Yutaka Hirano

unread,
Nov 21, 2021, 11:11:43 PM11/21/21
to guest271314, web-transport-dev
Can you fork https://github.com/GoogleChrome/samples/ and make edits to the repository so that we can reproduce the problem more easily?

--
You received this message because you are subscribed to the Google Groups "web-transport-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web-transport-...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/web-transport-dev/3fbb12c0-e8e8-4ee7-a230-529e578509fan%40chromium.org.
Message has been deleted

guest271314

unread,
Nov 22, 2021, 11:08:01 AM11/22/21
to web-transport-dev, yhi...@chromium.org, web-transport-dev, guest271314
https://github.com/guest271314/samples-1. Enter '1' in the textarea and send the datagram. This issue is similar https://github.com/aiortc/aioquic/issues/148. I am not sure how to use transmit(). 

guest271314

unread,
Nov 24, 2021, 2:03:56 AM11/24/21
to web-transport-dev, guest271314, yhi...@chromium.org, web-transport-dev
Compare streaming with fetch() and PHP directly

<?php
  header('Vary: Origin');
  header("Access-Control-Allow-Origin: *");
  header("Access-Control-Allow-Methods: POST");
  header("Content-Type: text/plain");
  header("X-Powered-By:");
  $input = fopen("php://input", "rb");
  stream_set_blocking($input, 0);
  echo passthru(stream_get_contents($input));
  fclose($input);
  exit(0);
?>

var abortable = new AbortController();
var {signal} = abortable;
var now = performance.now();
fetch('http://localhost:8000/index.php', {method: 'post', body:'parec -d alsa_output.pci-0000_00_14.2.analog-stereo.monitor', signal})
.then((r) => r.body)
.then((readable) => readable.pipeTo(new WritableStream({
  write(v) {console.log(v)},
  close() {console.log('Stream closed.')}
})).then(() => `Done streaming at ${(performance.now() - now) / 1000}.`))
.then(console.log)
.catch(console.warn);

same fetch() call with Python serving the stream

fetch('http://localhost:8000/index.php', {method: 'post', body:'./stream.py', signal})
// ...

#!/usr/bin/env -S python3 -u
import sys
import json
import struct
import os
import subprocess
from shlex import split

cmd = 'parec', '-d', 'alsa_output.pci-0000_00_14.2.analog-stereo.monitor'
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
for chunk in iter(lambda: process.stdout.read(512), b''):
    if chunk is not None:
        sys.stdout.buffer.write(chunk)
        sys.stdout.buffer.flush()  

Yutaka Hirano

unread,
Nov 24, 2021, 6:52:29 AM11/24/21
to guest271314, web-transport-dev
Sorry I cannot use parec on my environment (WSL1).

I can, however, see the datagrams coming from the server by replacing the chunk generating code. https://github.com/yutakahirano/samples-1/tree/remove-parec

Hope this helps,

guest271314

unread,
Nov 24, 2021, 9:30:14 AM11/24/21
to Yutaka Hirano, web-transport-dev
How long did the code run before stopping?

On linux the code halts with

   Connection failure: Connection terminated

guest271314

unread,
Nov 24, 2021, 9:36:38 AM11/24/21
to web-transport-dev, guest271314, web-transport-dev, yhi...@chromium.org
We should not have to call write() but once.

The flow chart should be

write() -> 1 time to datagram/unidirectional/birectional stram to supply command to run in a shell or directly in the server
read() -> potentially indefinite stream (consider live-stream audio output, spech synthesis, or a radio station)

The only read I call write() more than once is because data stops streaming to the browser if I don't.

Again, even then the server halts with Connection failure.

I suggest you folks actually test streaming an indefinite stream at WPT and internally instead of 1 or 2 bytes.

guest271314

unread,
Nov 24, 2021, 10:06:02 AM11/24/21
to web-transport-dev, guest271314, web-transport-dev, yhi...@chromium.org
That code winds up freezing the OS. Another case I encountered while trying to stream with WebTransport.

Yutaka Hirano

unread,
Nov 24, 2021, 10:29:08 AM11/24/21
to guest271314, web-transport-dev
I ran the client and server for 5 minutes and it is still running. They continue sending datagrams and I haven't seen a connection error.

On the client side, we can reduce the CPU load greatly with removing the DOM operation and console.log.

guest271314

unread,
Nov 26, 2021, 4:42:15 PM11/26/21
to web-transport-dev, yhi...@chromium.org, web-transport-dev, guest271314
The problem with the test pattern is we wind up calling 'subprocess' again each time write() is called. This can't be the canonical procedure to stream using datagrams. We have not even gotten to the stalling of using bidirectional and unidirectional streams yet.

How would you achieve the requirement?

guest271314

unread,
Nov 26, 2021, 7:56:58 PM11/26/21
to web-transport-dev, guest271314, yhi...@chromium.org, web-transport-dev
I created trying-to-stream-1 branch https://github.com/guest271314/samples-1/tree/guest271314-trying-to-stream-1/webtransport with latest test using transmit() and only 1 write() call. Only 28 datagrams of 512 length reach browser. 

Why can we only send N datagrams per write()?

guest271314

unread,
Nov 28, 2021, 1:45:30 PM11/28/21
to web-transport-dev, guest271314, yhi...@chromium.org, web-transport-dev
When you run the code in this branch https://github.com/guest271314/samples-1/blob/guest271314-trying-to-stream-2/webtransport/ - with the OS Task Manager open - you will notice that RSS 

  • RSS (Resident Set Size [of Memory]) is that section of the Virtual Memory that's actually in RAM. This number can contain SHR. Hence, the sum of RSS values of two processes need not be the actual RAM they're consuming. So, for example, consider a process that has forked off a child. Until either process changes something, they share the same Resident Set (they just don't know it due to Virtual Memory).

continues to grow. 

Whatever internal buffer is being used is never cleared/flushed after send_datagram() and transmit() are called in the server.

Again, the pattern is not ideal itself.

We should only need to all write() from client once, thereafter the indefinite/infinite stream from server should actually stream to client, not be held in some buffer.

Yutaka Hirano

unread,
Nov 28, 2021, 9:29:57 PM11/28/21
to guest271314, web-transport-dev
On Sat, Nov 27, 2021 at 6:42 AM guest271314 <guest...@gmail.com> wrote:
The problem with the test pattern is we wind up calling 'subprocess' again each time write() is called.
I'd say it is a problem in your example, not in aioquic or GoogleChrome/samples or Chromium.
 
This can't be the canonical procedure to stream using datagrams. We have not even gotten to the stalling of using bidirectional and unidirectional streams yet.

How would you achieve the requirement? 

I recognize this as asking for help rather than reporting an issue. 
I agree with you that running a subprocess every time it gets a datagram doesn't sound right. Probably you want to run the subprocess when the session is established (please see "_handshake_webtransport").

Also, you need to handle the output of the subprocess "asynchronously", which means you shouldn't block aioquic for too long (with "read", typically). Using os.set_blocking (only) is not a solution.
I'm not at all an expert here, but python asyncio and subprocess will probably help you.

Hope this helps,

guest271314

unread,
Nov 28, 2021, 10:27:59 PM11/28/21
to Yutaka Hirano, web-transport-dev
Right now the requirement does not appear to be possible using WebTransport.

The claim of WebTransport is flexible streaming. I have not observed that to be the case in my own testing and cannot point to any evidence in the wild substantiating that claim.

I am not necessarily asking for help. I am testing your gear. The 1 and 2 byte WPT tests do not test indefinite streaming. I filed https://github.com/aiortc/aioquic/issues/242 in aioquic (where handshake is still broken). I would suggest creating such a test case (indefinite/infinite streaming with one initial write() and that's all) in WPT, so you will know where you are re limitations. 

I achieve the requirement of live-streaming right now using Native Messaging with a Python host using 11MB, aioquic uses 25MB, or using fetch() with PHP. I have not achieved the requirement at all using WebTransport, and no such example exists that I am aware of.

Kind regards,
/guest271314/

guest271314

unread,
Jan 22, 2022, 3:27:58 PM1/22/22
to web-transport-dev, guest271314, web-transport-dev, yhi...@chromium.org
I just happened to be reading chrome://net-export on https://netlog-viewer.appspot.com/#quic and noticed

Idle Connection Timeout In Seconds | 30
Reduced Ping Timeout In Seconds | 15
...
Force Head of Line Blocking | false

I have not found any flags to disable QUIC timeouts.

Is this why the connection closes prematurely when attemptingto stream to the browser?Screenshot_2022-01-22_20-18-01.png

guest271314

unread,
Feb 2, 2022, 9:41:36 AM2/2/22
to web-transport-dev, guest271314, web-transport-dev, yhi...@chromium.org
How to disable idle timeout?
Reply all
Reply to author
Forward
0 new messages