Tainted canvas when accessing HLS live stream on Galaxy Ace 2 - nginx

I'm using avconv (ffmpeg) and nginx to stream frames from a camera over HLS and RTMP. Since my phone doesn't support flash it uses HTML5 video tags and HLS in order to stream the video. One feature that I'm trying to support is to record the live stream and save that to a file. However, I am unable to record the stream due to a cross-domain issue.
The live stream is coming from my machine on port 8080 (I'm referencing it using my internal IP, 10.150.x.x:8080/hls/mystream.m3u8) and the server is run on my machine through port 8000 (also referenced through internal IP). Because they are on different ports it is still viewed as cross domain.
In my nginx.conf I have added Access-Control-Allow-Origin: *
and I've also tried adding Access-Control-Allow-Methods GET, PUT, POST, DELETE, OPTIONS
and Access-Control-Allow-Headers Content-Type, Authorization, X-Requested-With
When I examine the headers using curl -I http://10.150.x.x:8080/hls/mystream.m3u8 and through firefox and chrome from my desktop I can see the appropriate headers. But when I look at the headers using the chrome dev tools for my phone I get "CAUTION: Provisional headers shown."
I attempt to capture the frames using canvas.toDataURL() and it is this function that is giving the security error.
Why is it that even though I have Access-Control-Allow-Origin: * in my nginx.conf I still get a cross domain issue?
nginx.conf:
#user nobody;
worker_processes 1;
error_log logs/error.log debug;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name 10.150.x.x;
#server_name bsid.ca;
add_header 'Access-Control-Allow-Origin' "*";
#add_header 'Access-Control-Allow-Methods' "GET, PUT, POST, DELETE, OPTIONS";
#add_header 'Access-Control-Allow-Headers' "Content-Type, Authorization, X-Requested-With";
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /path/to/nginxVideo;
}
# sample handlers
#location /on_play {
# if ($arg_pageUrl ~* 127.0.0.1) {
# return 201;
# }
# return 202;
#}
#location /on_publish {
# return 201;
#}
#location /vod {
# alias /var/myvideos;
#}
# rtmp stat
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
# you can move stat.xsl to a different location
root /usr/build/nginx-rtmp-module;
}
# rtmp control
location /control {
rtmp_control all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
rtmp {
server {
listen 1935;
ping 30s;
notify_method get;
application myapp {
live on;
# sample play/publish handlers
#on_play http://127.0.0.1:8080/on_play;
#on_publish http://127.0.0.1:8080/on_publish;
# sample recorder
#recorder rec1 {
# record all;
# record_interval 30s;
# record_path /tmp;
# record_unique on;
#}
# sample HLS
hls on;
hls_path /home/richard/Media/nginxVideo/hls;
hls_base_url http://10.150.x.x:8080/hls/;
hls_sync 2ms;
}
# Video on demand
#application vod {
# play /var/Videos;
#}
# Video on demand over HTTP
#application vod_http {
# play http://127.0.0.1:8080/vod/;
#}
}
}
Full error:
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
UPDATE
After a lengthy discussion with Ray Nicholus it was determined that the issue was that I was setting the crossorigin attribute on my video element after the stream had begun. By setting it earlier I was able to access the frames without the need of a proxy.

Dev tools will not reveal most specifics about the request if it believes the response has not properly acknowledged the cross-origin request. All I can think of is that you are setting the crossorigin attribute after the bytes have started to stream in (at which point the video is already tainted), either that or your server is not properly acknowledging the request. If the request is lacking an Origin header, the former is likely the case.

Related

How to redirect user if direct access image files by browser? [nginx]

How do I redirect if a user tries to direct access image files in browser only? I want to still keep the ability to allow social media sites to embed our images by hotlinking. I just want only if a user direct access image by browser to redirect.
This is my nginx conf
proxy_cache_path /var/www/img.example.com/htdocs/cache-store levels=1:2 keys_zone=pixstore:10m max_size=5g inactive=7d use_temp_path=off;
server {
server_name img.example.com www.img.example.com;
access_log /var/log/nginx/img.example.com.access.log ;
error_log /var/log/nginx/img.example.com.error.log;
add_header X-Proxy-Cache $upstream_cache_status;
location / {
proxy_cache pixstore;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://xxx.xxx.xxx.xxx:8090;
proxy_redirect off;
include proxy_params;
proxy_cache_valid 200 7d;
proxy_cache_valid 404 5m;
}
location ~ "^/c/600x1200_90_webp/img-master/img/\d+/\d+/\d+/\d+/\d+/\d+/((?<filenum>\d+)[^/]+\.(jpg|png|webp))$" {
valid_referers server_names;
proxy_pass http://xxx.xxx.xxx.xxx:8090;
if ($invalid_referer = "0") {
return 301 http://view.example.com/artwork/$filenum; }
}
}
The redirect isn't working. How can I fix this?
I would consider referer. Here is nginx module, and here is an article with some explanation, and gist with piece of code. So basically - you need to have the module and then you can use something like this:
# apply this rule on any location that’s an image using Regexp
location ~* \.(png|gif|jpg|jpeg|swf|ico)(\?[0-9]+)?$ {
# block empty blocked or whiteliste referers
valid_referers none blocked ~\.example.com ~\.google\. ~\.yahoo\. ~\.bing\. ~\.facebook\. ~\.fbcdn\.;
if ($invalid_referer) {
return 403;
}
}
Where example.com is your domain. Let me know how it goes - I'll update answer if needed.
I think you would be a lot better off doing it with something like Node.js and testing the User Agent string against a regular expression and if it contains something that browsers have like the text "Chrome" "Firefox" etc in it then redirect.
How about whitelisting social media's agent IP instead?
For example, this is how you find all IP address used by Facebook's agent
whois -h whois.radb.net -- '-i origin AS32934' | grep ^route
then add this to your nginx conf
location ~ /(?<filenum>\d+)[^/]*\.(jpg|png|webp)$ {
allow 69.63.176.0/20;
allow 66.220.144.0/20;
...
deny all;
error_page 403 http://view.example.com/artwork/$filenum;
}
And perhaps, you may want to check you regex using this site

How to trigger nginx-rtmp pull from the ingest server on client requests a .m3u8 video through load balancer

I have a ingest nginx-rtmp server which act as a single source of rtmp streams for worker nodes.
Ingest instance gets the rtmp through OBS. Now when client request a video through load balancer like https://load-balancer.com/hls/test.m3u8
Load balancer sends this request to one of the worker nodes.
The worker nodes has following nginx config file.
worker_processes auto;
events {
worker_connections 1024;
}
# RTMP configuration
rtmp {
server {
listen 1935; # Listen on standard RTMP port
chunk_size 4000;
# Define the Application
application show {
live on;
pull rtmp://localhost:1935/stream/ live=1;
# Turn on HLS
hls on;
hls_path /mnt/hls/;
hls_fragment 3;
hls_playlist_length 60;
# disable consuming the stream from nginx as rtmp
deny play all;
}
}
}
http {
sendfile off;
tcp_nopush on;
aio on;
directio 512;
default_type application/octet-stream;
server {
listen 8080;
location / {
# Disable cache
add_header 'Cache-Control' 'no-cache';
# CORS setup
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length';
# allow CORS preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
types {
application/dash+xml mpd;
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /mnt/;
}
}
}
The nginx-rtmp pull of this worker node will not work until anyone request for it. Hence it will not generate the hls file for the http request.
Note: I can not push the stream to the worker nodes as the worker noders are being auto scalled and does not have a fix number.
I wasn to know a solution by which, Whenever a client request a video which and the worker node gets the https request, Some how the rtmp server application show gets a hit and pull the video from the ingest server and generates the hls block and the http request can be fulfilled.

Live stream stops when a second viewer joins

I am trying to build a live stream platform using nginx and nginx-http-flv-module (with nginx-rtmp-module).
I have been using nginx-http-flv-module's guide.
I built an nginx server with rtmp and http-flv support.
My nginx.conf file:
#user nobody;
worker_processes 1;
error_log logs/error.log debug;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name localhost;
...
location /live {
flv_live on; #open flv live streaming (subscribe)
chunked_transfer_encoding on; #open 'Transfer-Encoding: chunked' response
add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header
add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header
add_header 'Access-Control-Expose-Headers' 'Content-Length';
}
}
}
rtmp {
server {
listen 1935;
ping 30s;
notify_method get;
application myapp {
live on;
}
}
}
I start publishing my stream using OBS and play the stream in the browser using flv.js like this:
<video id="videoElement" controls autoplay></video>
...
<script>
let videoElement = document.getElementById('videoElement');
let flvPlayer = flvjs.createPlayer({
type: 'flv',
isLive: "true",
url: 'http://192.168.1.122:8080/live?port=1935&app=myapp&stream=test'
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
</script>
And everything works great! The stream is playing in the browser as expected. But the problem is whenever a second viewer starts watching the stream (if I open it in another browser tab f.e). The player stops playing and starts infinite loading. So what can cause this problem?
I am the owner of nginx-http-flv-module, I am sorry that the bug was caused by the commit on July 7, 2019 and it has been fixed already.
You can try the latest code.

Setting up nginx to stream HLS/RTMP simultaneously

I have an nginx web server with the RTMP module running perfectly and pushing out video to several RTMP destinations (Facebook, YouTube, Periscope, etc.).
I now need the stream to output in the HLS protocol so I can build a custom player for it without flash dependency. I've tried following a few other tutorials, but I am struggling.
I have the default config file that came with the installation of nginx with the following added to the end:
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
record off;
push rtmp://<streaming-service/key>
}
}
}
What exactly do I need to add to this config file to retain my ability to push out video to these RTMP destinations as well as have a HLS feed I can use in a player without Flash?
I already have FFMPEG installed on the machine in order to send video to Periscope. I've seen solutions that do not need FFMPEG, but I just wanted to add that I did have it installed and am somewhat familiar with it.
EDIT: for more information, I am sending video through the server via a Teradek encoder.
I solved this with FFMPEG. The pull directive wasn't working properly. Here's the config file that worked for me:
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server {
listen 80;
server_name localhost;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /tmp/;
add_header Cache-Control no-cache;
add_header 'Access-Control-Allow-Origin' '*';
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
record off;
push rtmp://<location/key>
exec ffmpeg -i rtmp://localhost/live/test -vcodec libx264 -vprofile baseline -x264opts keyint=40 -acodec aac -strict -2 -f flv rtmp://localhost/hls/test;
}
application hls {
live on;
hls on;
hls_path /tmp/hls/;
hls_fragment 6s;
hls_playlist_length 60s;
}
}
}

Nginx loosing header sometime

I am using nginx as reverse proxy server for android application(get/post requests only). Some of the data contained in the headers. In some cases nginx loses "id" or "fail_id" header.
config:
user user;
worker_processes 4;
error_log /var/log/nginx/error.log;
events {
worker_connections 100000;
use epoll;
}
http {
upstream myproject {
server 192.168.88.246:2053;
}
server {
listen 2054;
ssl on;
ssl_certificate /home/user/android/cert/cert.pem;
ssl_certificate_key /home/user/android/cert/key.pem;
proxy_read_timeout 600;
proxy_send_timeout 600;
location / {
proxy_pass http://myproject;
proxy_pass_request_headers on;
}
}
}
Could i set original headers of request?
Updated:
A more detailed study found that nginx miss "fail_id" header. All other headers are working.
Problem solved!
Nginx default config misses headers with underscore.
This directive solved the problem:
underscores_in_headers on;
Thank You For the underscore directive. I have used underscores_in_headers on; directive and the header value with underscore passed to my node app.
Now I am able to access header value (api_key) using postman request and angular request from the web.
But now when The request is raise from the android app and I have set the api_key in the android request header, I am unable to access the api_key.
My config is:
server {
listen 80;
server_name uat.api.myserver.com;
underscores_in_headers on;
location / {
proxy_pass http://localhost:9102;
}
}

Resources