0

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.

Rick
  • 563
  • 3
  • 6
  • 24
  • It's possible that the browser simply doesn't support the crossorigin attribute on video elements, either that or you've forgotten to include that attribute on your video element. – Ray Nicholus Apr 24 '14 at 17:54
  • The browser is chrome so I assume that it supports the crossorigin attribute and I've set the `crossOrigin` attribute to `anonymous`. That being said I'm not sure if that's the correct thing to do. – Rick Apr 24 '14 at 18:01
  • Does it work on any other browser? – Ray Nicholus Apr 24 '14 at 18:02
  • Nitpick: BTW the attribute is "crossorigin". "crossOrigin" is the property name. – Ray Nicholus Apr 24 '14 at 18:03
  • The stream isn't visible from the default android browser nor for Opera. I haven't done much research into it but my best guess would be that they have difficulties supporting HLS. – Rick Apr 24 '14 at 18:09
  • There's no guarantee that the browser you are using supports the crossorigin attr on video elements, just because it's Chrome. Mobile Chrome often has different limitations than desktop Chrome. One way to test for this support may be to check if the crossOrigin property on a generic video element is defined. For example: `document.createElement("video").crossOrigin !== undefined` – Ray Nicholus Apr 24 '14 at 18:14
  • I tried both your method and the method found here -> http://stackoverflow.com/questions/1641507/detect-browser-support-for-cross-domain-xmlhttprequests and they both suggested that chrome mobile supports cross origin. The second test suggests that it supports it with credentials specifically. – Rick Apr 24 '14 at 18:18
  • I see, have you looked at the response headers via a proxy to ensure the proper CORS header is being included? – Ray Nicholus Apr 24 '14 at 18:20
  • Your exact code returns true, if I try `document.createElement('video').crossOrigin` it returns an empty string. I assume that that would be expected behaviour. – Rick Apr 24 '14 at 18:22
  • Yes, that indicates support. Have you looked at the response headers via a proxy to ensure the proper CORS header is being included? – Ray Nicholus Apr 24 '14 at 18:23
  • I've attempted running `corsproxy 0.0.0.0 9292` in order to reference the stream and it is the same result. I check the headers using curl to find that access control origin is * but using the chrome dev tools I am unable to see the headers. I should note that it also says access-control-allow-credentials: true. Does this mean I need to setup some kind of key? – Rick Apr 24 '14 at 18:26
  • Yep, 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, 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. – Ray Nicholus Apr 24 '14 at 18:29
  • Thanks, you were right, I was setting the crossorigin attribute too late. – Rick Apr 24 '14 at 18:35
  • I'll make that the answer then. Glad it's working for you now. – Ray Nicholus Apr 24 '14 at 18:36

1 Answers1

1

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.

Ray Nicholus
  • 19,538
  • 14
  • 59
  • 82