wandenberg / nginx-push-stream-module

A pure stream http push technology for your Nginx setup. Comet made easy and really scalable.
Other
2.22k stars 295 forks source link

[question] Handling chunked POST bodies sent to /pub endpoints #312

Open jaygooby opened 3 months ago

jaygooby commented 3 months ago

I'm trying to use ffmpeg's -progress URL option to send rendering progress data to a /pub channel.

Here's a simple example that generates 30 seconds of black h264 video:

ffmpeg -y -progress "http://127.0.0.1/pub?id=ffmpeg" -t 30 -f lavfi -i color=c=black:s=640x480 -c:v libx264 -tune stillimage -pix_fmt yuv420p black.mp4

The -progress URL is a /pub channel: http://127.0.0.1/pub?id=ffmpeg

My nginx location looks like:

location = /pub {

  # We're not using a proxy, just the push-stream module
  # proxy_buffering off;
  # proxy_set_header Connection "";
  # ensure http 1.1 or buffering always occurs
  # proxy_http_version 1.1;

  # we don't care about client max body size
  client_max_body_size 0;

  # don't do this, nothing comes through at all
  # client_body_buffer_size 0;

  # should be on by default
  chunked_transfer_encoding on;

  # turn off client request buffering
  proxy_request_buffering off;

  # activate publisher (admin) mode for this location
  push_stream_publisher admin;

  # query string based channel id
  push_stream_channels_path $arg_id;

  # access control
  allow all; # don't do this in prod!
}

ffmpeg sends its progress data as a chunked POST body. I've tested that it sends as it happens, by capturing its output with netcat:

# start "server"
nc -l 7000 > ffmpeg.http.log &

# render 30 seconds of blank h264 sending progress to the server
ffmpeg -y -progress http://127.0.0.1:7000 -t 30 -f lavfi -i color=c=black:s=640x480 -c:v libx264 -tune stillimage -pix_fmt yuv420p black.mp4

# cat the "server" log
cat ffmpeg.http.log

You can see the chunked POST headers, followed by the data:

POST / HTTP/1.1
Transfer-Encoding: chunked
User-Agent: Lavf/58.29.100
Accept: */*
Connection: close
Host: 127.0.0.1:7000
Icy-MetaData: 1

c5
frame=346
fps=0.00
stream_0_0_q=28.0
bitrate=   0.0kbits/s
total_size=48
out_time_us=11840078
out_time_ms=11840078
out_time=00:00:11.840078
dup_frames=0
drop_frames=0
speed=23.7x
progress=continue

c7
frame=708
fps=704.69
stream_0_0_q=28.0
bitrate=   0.0kbits/s
total_size=48
out_time_us=26320078
out_time_ms=26320078
out_time=00:00:26.320078
dup_frames=0
drop_frames=0
speed=26.2x
progress=continue

c5
frame=750
fps=680.10
stream_0_0_q=-1.0
bitrate=   7.7kbits/s
total_size=28599
out_time_us=29880078
out_time_ms=29880078
out_time=00:00:29.880078
dup_frames=0
drop_frames=0
speed=27.1x
progress=end

0

The problem I've got is that with that ^^^ nginx location block, nothing appears in the channel until ffmpeg closes the connection. I've tried with the commented out proxy_ directives too, just in case. Any ideas about what's missing so the client's POST body isn't buffered?

sunnychun commented 3 months ago

您的信已收到,我将尽快给您答复

wandenberg commented 3 months ago

Hi @jaygooby it looks like the issue is because ffmpeg is using a chunked post, meaning it opens a connection and sends data without closing the connection. The module is not designed for that. Chunked posts can be misinterpreted, like where the real message starts/finishes. I don't know this feature of ffmpeg. Try to check if it has options to not use chunked posts. If it does not, I guess that the solution would be to do a simple custom application to listen to the ffmpeg post, break down each group of progress notifications, and then post to the nginx using the module to broadcast it to all your subscribers. Please, let me know if this makes sense for you. I can try to help with this custom application.

jaygooby commented 3 months ago

@wandenberg thanks for the confirmation :(

I know there's no way to disable the ffmpeg chunked POST, so I'll see what else I can do…

ffmpeg can also stream its progress to a pipe or fifo; I'll see if I can intercept the data that way and POST each update separately