kaltura / nginx-vod-module

NGINX-based MP4 Repackager
GNU Affero General Public License v3.0
1.98k stars 439 forks source link

mp4_metadata_reader_read: buffer size is smaller than moov size #1316

Open mhowell86 opened 2 years ago

mhowell86 commented 2 years ago

I get this error for nearly every file I play. Regardless of the settings I can only play videos under 3 minutes long. What am I doing wrong?

2021/11/04 06:04:32 [error] 7#7: *12 mp4_metadata_reader_read: buffer size 130984 is smaller than moov size 215742 while reading media header

mhowell86 commented 2 years ago

I suspect it has something to do with running this in docker for windows with WSL2. I don't get the same issue when running on Linux.

erankor commented 2 years ago

As I see it, there are two possible reasons for this error -

  1. The file is corrupt (truncated) - I guess this is not the case, since you wrote that it works on Linux...
  2. The read operation returns incomplete - for example, the module tries to read 256k, but gets back only 128k, even though it's not the end of the file. AFAIK, in Linux, this is not possible - the operating system will fill up the buffer if it has enough data. But maybe this assumption does not hold on docker/WSL2... You can try to run the module with debug logs enabled (--with-debug & debug severity in error_log directive), it should be visible there.
skyl commented 1 year ago

I'm on kubernetes, trying to use gcs-proxy. These are the debug logs I have around the error message: logs.txt

It seems to be making it through the vod hls location:

    location ~ ^/uploadz/public/hls/(?<username>[^/]+)/(?<filename>[^/]+)/[^/]*\.m3u8$ {
        vod hls;
        vod_mode remote;
        vod_upstream_location /gcsproxy;
        vod_upstream_extra_args "username=$username&filename=$filename";
        vod_metadata_cache metadata_cache 512m;
        vod_response_cache response_cache 128m;
        vod_cache_buffer_size 512k;
        vod_output_buffer_pool 64k 128;

        # vod_hls_master_file_name_prefix "master";
        # vod_hls_absolute_index_urls on;
        # GPT said ...
        vod_hls_absolute_iframe_urls on;
    }

and on to the upstream:

    location /gcsproxy/ {
        internal;
        proxy_pass http://gcsproxy/$arg_username/$arg_filename.mp4;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Range $http_range;
        proxy_set_header Connection "";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
    }

The backend is https://github.com/bukukasio/gcs-proxy - that seems to be working fine ...

Anything obvious I might try?

erankor commented 1 year ago

The log seems incomplete, and out-of-order (it seems "newer" lines appear first in the file, but i'm not sure the order of lines written at the same second is retained, flipping the lines may not produce correct results). I need a complete log of the request, with the lines ordered correctly - as written by nginx (=not pulled from some log collector or something)

skyl commented 1 year ago

Here is the complete log of one request to the vod hls location: vod-log.txt

Thanks for any help @erankor !

skyl commented 1 year ago

The gcsproxy, for the one request I make, logs:

time="2023-03-29T05:33:55Z" level=debug msg="finished handling request" ellapsed=85.544704ms method=GET path=/free2z/tiktok-tecnopapapi.mp4 proxyEndpoint= response=206
time="2023-03-29T05:33:55Z" level=debug msg="finished handling request" ellapsed=34.814168ms method=GET path=/free2z/tiktok-tecnopapapi.mp4 proxyEndpoint= response=206
erankor commented 1 year ago

From the log, it seems there were 2 requests to the upstream, both have Range: bytes=0-4095. The first request is expected to use this range, but the second request is expected to be something like 4k-36k (according to the moov size in the log). The problem is probably this line in the conf proxy_set_header Range $http_range; - the value of the $http_range variable is probably cached on the first subrequest, and therefore it doesn't change in the second one. Try to remove this line...

amnonbb commented 1 year ago

For me was fix to raise vod_max_metadata_size from default 128m to 256m

skyl commented 1 year ago

Thanks @erankor that seemed to work! If you have a Zcash wallet address, I'll send you one for the help. Helped a lot. GPT4 really seemed to like the idea of proxy_set_header Range $http_range;. Is that totally erroneous? The current setup seems to support seek and scrub pretty well without it. I'm basically trying to do what NYTimes described a couple of years ago:

    # Define the location where Nginx-vod will process the requests
    location ~ ^/uploadz/public/hls/(?<username>[^/]+)/(?<filename>[^/]+)/.+ {
        add_header Cache-Control "public, max-age=31536000";
        # access_log /var/log/nginx/location1_access.log;
        # error_log /var/log/nginx/location1_error.log debug;
        vod hls;
        vod_mode remote;
        # Apple recommends 6 seconds? default it 10?
        # vod_segment_duration 1000;
        vod_segment_duration 6000;
        vod_upstream_location /gcsproxy;
        vod_upstream_extra_args "username=$username&filename=$filename";
        vod_metadata_cache metadata_cache 1024m;
        vod_response_cache response_cache 128m;
        vod_cache_buffer_size 1024k;
        vod_output_buffer_pool 128k 256;
        vod_hls_absolute_iframe_urls on;
        vod_hls_absolute_master_urls on;
        vod_max_metadata_size 256m;
        vod_base_url https://$http_host/uploadz/public/hls/$username/$filename/;
    }

    location /gcsproxy/ {
        internal;
        proxy_pass http://gcsproxy/$arg_username/$arg_filename.mp4;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        # proxy_set_header Range $http_range;
        proxy_set_header Connection "";
        proxy_buffer_size 256k;
        proxy_buffers 8 512k;
        proxy_busy_buffers_size 512k;
    }

I'm new to this module; so, if anyone sees anything weird/wrong with my setup let me know. It's currently just guess and test with help from GPT and @erankor and the README ...

erankor commented 1 year ago

A few comments -

  1. Better use nginx's expires directive or this module's vod_expires directive, instead of setting cache-control directly
  2. The default of vod_hls_absolute_master_urls is on so this line has no impact
  3. Better move directives such as vod_metadata_cache / vod_response_cache / ... to higher level (under http or server), if you'll want to add DASH in the future, for example, it makes sense to share the caches.
  4. I don't think the proxy buffer directives have any effect here, since the module manipulates the buffers when it calls this location, better align the /gcsproxy with the remote sample configuration.
  5. As a general rule - better use the minimum configuration you need, if it works without some directive - remove it, unless you have a good reason to set it (the cache directives are an exception to this - it will work without them, but it's highly recommended to use caching...).

About proxy_set_header Range - yes, it's wrong. By default nginx proxies all headers to the upstream server, the range header will be proxied also without this line. And, as we've seen this has the side-effect of making all subrequest use the same range due to nginx's variable caching.