home-assistant-libs / pychromecast

Library for Python 3 to communicate with the Google Chromecast.
MIT License
2.53k stars 377 forks source link

Cannot play HLS (m3u8 example) #672

Closed JonasVautherin closed 1 year ago

JonasVautherin commented 1 year ago

For some reason I fail to play HLS on my Chromecast. I'm following the pychromecast readme until this point:

mc.play_media('https://devimages.apple.com.edgekey.net/iphone/samples/bipbop/bipbopall.m3u8', 'application/x-mpegurl')

Which fails, but I don't know why (and pychromecast does not show any log, right?).

However the URL works with, e.g. mpv:

$ mpv https://devimages.apple.com.edgekey.net/iphone/samples/bipbop/bipbopall.m3u8

I also tried creating an HLS (m3u8) setup locally with two other videos. For the first one, checking one of the *.ts chunks with ffmpeg -i, I see:

Input #0, mpegts, from 'segment00001.ts':=    0KB sq=    0B f=0/0   
  Duration: 00:00:12.60, start: 3605.630622, bitrate: 1731 kb/s
  Program 1 
    Stream #0:0[0x41]: Video: h264 (High 10) (HDMV / 0x564D4448), yuv420p10le(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 90k tbn, 47.95 tbc

For another video that I converted the same way:

Input #0, mpegts, from 'segment00000.ts':
  Duration: 00:00:14.01, start: 3600.000000, bitrate: 1913 kb/s
  Program 1 
    Stream #0:0[0x41]: Video: h264 (High) (HDMV / 0x564D4448), yuv420p(tv, bt709, progressive), 1280x720 [SAR 1:1 DAR 16:9], 59.94 fps, 59.94 tbr, 90k tbn, 119.88 tbc

Am I missing something?

JonasVautherin commented 1 year ago

Feels like it may be related to CORS on the server side. According to the Chromecast docs:

For adaptive media streaming, Google Cast requires the presence of CORS headers, but even simple mp4 media streams require CORS if they include Tracks. If you want to enable Tracks for any media, you must enable CORS for both your track streams and your media streams. So, if you do not have CORS headers available for your simple mp4 media on your server, and you then add a simple subtitle track, you will not be able to stream your media unless you update your server to include the appropriate CORS headers.

You need the following headers: Content-Type,Accept-Encoding, and Range. Note that the last two headers, Accept-Encoding and Range, are additional headers that you may not have needed previously.

Wildcards "*" cannot be used for the Access-Control-Allow-Origin header. If the page has protected media content, it must use a domain instead of a wildcard.

For my local video, I try to serve with:

#!/usr/bin/env python3

# It's python3 -m http.server PORT for a CORS world

from http.server import HTTPServer, SimpleHTTPRequestHandler
import sys

class CORSRequestHandler(SimpleHTTPRequestHandler):
    def end_headers(self):
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', '*')
        self.send_header('Access-Control-Allow-Headers', '*')
        self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
        self.send_header('Content-Type', 'text/plain charset=UTF-8')
        self.send_header('Accept-Encoding', '*')
        self.send_header('Range', 0)
        return super(CORSRequestHandler, self).end_headers()

    def do_OPTIONS(self):
        self.send_response(200)
        self.end_headers()

host = sys.argv[1] if len(sys.argv) > 2 else '0.0.0.0'
port = int(sys.argv[len(sys.argv)-1]) if len(sys.argv) > 1 else 8080

print("Listening on {}:{}".format(host, port))
httpd = HTTPServer((host, port), CORSRequestHandler)
httpd.serve_forever()

But I'm certain that my Range header is wrong, and I am not sure of the others. Also I don't know what to put in Access-Control-Allow-Origin instead of *.

Any idea there?

JonasVautherin commented 1 year ago

Probably not related to pychromecast anyway.

a-box commented 1 year ago

Probably not related to pychromecast anyway.

Did you find a solution to make HLS m3u8 without CORS work with pyChromecast?