-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DIAL with lounge API #10
Comments
One last thing I want to mention, is that Roku platform is pretty limited, and that's why I've gone through this route. I haven't worked on Android TV, but there might be better ways to do things. |
I'm glad that I have helped. Interesting info, friend. Thanks for sharing.
Regards, Yuriy
…On Sat, Feb 10, 2024, 18:45 Brahim Hadriche ***@***.***> wrote:
Hey! I'm the author of Playlet <https://github.com/iBicha/playlet>, a
YouTube app for Roku TV.
I recently embarked on the journey to support casting from the YouTube app
using lounge.
Some projects, like https://github.com/henriquekano/youtube-lounge-api,
https://github.com/yuliskov/MediaServiceCore, and (especially)
https://github.com/patrickkfkan/yt-cast-receiver helped me understand the
protocol better, so I wanted to say thanks, and also share a bit of my
findings in case it is useful.
As I tried to understand the DIAL spec, which is the "Link using Wifi"
option, I found it was mostly working like this:
Chain of events
1. Devices (mobile) broadcast an SSDP
<https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol>
message M-SEARCH
M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: 5
ST: urn:dial-multiscreen-org:service:dial:1
1. And devices (TVs) capable of accepting casting would respond with
something like
Cache-Control: max-age=3600
ST: urn:dial-multiscreen-org:service:dial:1
USN: ID::urn:dial-multiscreen-org:service:dial:1
Ext:
Server: Roku/12.5.5 UPnP/1.0 Roku/12.5.5
LOCATION: http://IP:8060/dial/dd.xml
WAKEUP: MAC=MAC;Timeout=10
Notice the LOCATION header. This would be a link to an xml file
containing the device description.
1. The device (mobile) makes an http GET request to the LOCATION. My
TV's response looks something like this
<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:roku-com:device:player:1-0</deviceType>
<friendlyName>TV</friendlyName>
<manufacturer>XXX</manufacturer>
<manufacturerURL>xxx.com</manufacturerURL>
<modelDescription>Roku Streaming Player Network Media</modelDescription>
<modelName>XXX</modelName>
<modelNumber>XXX</modelNumber>
<modelURL>http://www.roku.com/</modelURL>
<serialNumber>XXXXXXX</serialNumber>
<UDN>uuid:XXXXXXXXXX</UDN>
<iconList>
<icon>
<mimetype>image/png</mimetype>
<width>360</width>
<height>219</height>
<depth>8</depth>
<url>device-image.png</url>
</icon>
</iconList>
<serviceList>
<service>
<serviceType>urn:roku-com:service:ecp:1</serviceType>
<serviceId>urn:roku-com:serviceId:ecp1-0</serviceId>
<controlURL></controlURL>
<eventSubURL></eventSubURL>
<SCPDURL>ecp_SCPD.xml</SCPDURL>
</service>
<service>
<serviceType>urn:dial-multiscreen-org:service:dial:1</serviceType>
<serviceId>urn:dial-multiscreen-org:serviceId:dial1-0</serviceId>
<controlURL></controlURL>
<eventSubURL></eventSubURL>
<SCPDURL>dial_SCPD.xml</SCPDURL>
</service>
</serviceList>
</device>
</root>
Notice how the service list contains
urn:dial-multiscreen-org:service:dial:1, which is saying the device is
capable of DIAL. Good.
In the response headers for hitting the device description
http://IP:8060/dial/dd.xml, there's one important header to capture:
Application-Url: http://IP:8060/dial
Because it is the URL used to check if a specific DIAL compatible app is
installed or not.
1. The device (mobile) makes a GET request to Application-Url +
/YouTube (case sensitive). In this case that would be
http://IP:8060/dial/YouTube. The response would be something like this:
<?xml version="1.0" encoding="UTF-8" ?>
<service
xmlns="urn:dial-multiscreen-org:schemas:dial" dialVer="2.1">
<name>YouTube</name>
<options allowStop="true" />
<state>stopped</state>
</service>
If the mobile device gets a valid response, then it will see the device
over wifi and offer to connect to it (it will show up as the friendlyName
from the device description file.)
Note: on my Roku, the request will be blocked, unless the Origin header
is set to https://www.youtube.com
1. The response from http://IP:8060/dial/YouTube Might be in different
state, and the mobile devices react differently based on the presence of
the additionalData field.
- In the case the xml does not contain additionalData (so similar to
the content from step 4)
- The mobile device would send a POST request to
http://IP:8060/dial/YouTube, which includes theme and pairingCode
url encoded in the body. The pairing code can be registered using
https://www.youtube.com/api/lounge/pairing/register_pairing_code
and then the mobile phone would join the lounge once the code is registered.
- In the case the xml contains additionalData, e.g. :
Example copied from https://github.com/henriquekano/youtube-lounge-api/wiki/Discovery
<service xmlns="urn:dial-multiscreen-org:schemas:dial">
<name>YouTube</name>
<options allowStop="false"/>
<state>running</state>
<link rel="run" href="http://192.168.x.y:58722//run"/>
<additionalData>
<brand>Sony</brand>
<model>PS4 Pro</model>
<screenId>qweqweqwe</screenId>
<theme>??</theme>
<deviceId>ssssssssss</deviceId>
<loungeToken>xxxxxxxx</loungeToken>
<loungeTokenRefreshIntervalMs>1500000</loungeTokenRefreshIntervalMs>
</additionalData>
</service>
Then the phone will not send a pairing code, and will just join the
lounge, since it has everything it needs already.
- In the case the xml contains additionalData, but not containing a
loungeToken, e.g :
<?xml version="1.0" encoding="UTF-8" ?>
<service
xmlns="urn:dial-multiscreen-org:schemas:dial" dialVer="2.1">
<name>YouTube</name>
<options allowStop="true" />
<state>stopped</state>
<additionalData>
<testXXXXXXX>xxxxxxx</testXXXXXXX>
<brand>XXX</brand>
<model>XXXX</model>
<yumi>XXXXXXXXXXXXXX</yumi>
<passiveSessionId>XXXXXXXXXXX</passiveSessionId>
</additionalData>
</service>
Now this is the part I haven't explored yet - but I'm guessing a
passiveSessionId is some form of new feature allowing the session to be
passive until a device connects to it.
1. One last thing, when <options allowStop="true" /> is returned, the
mobile device (actually I tested this with Chrome browser) will a send a
DELETE request to close the app. Just a small detail.
Implementation
So the device is responsible for responding to the M-SEARCH requests, and
for providing a device description xml file. So by definition, only one app
can have the identifier YouTube for DIAL. This is why when the YouTube
mobile app casts to a device, the official YouTube tv app will launch. This
is why I went a different route.
1.
A DIAL server
I have a udp connection listening for broadcast messages. When it sees
an M-SEARCH, it responds just like the device does (see step 2 in the
first section), but I set the LOCATION here to point to a file that
I'm serving on a local web server running on the app.
2.
Serving device description
Using a local server, I'm service a "virtual device description" xml
file, and also serve an xml file for the/dial/YouTube file.
For the device description, I set the friendlyName to "Playlet on" +
tvName so that users can Identify the app they want to cast to.
So far I'm have a working implementation (just a PoC that needs cleanup)
here iBicha/playlet#276 <iBicha/playlet#276> that
does what I'm describing. I thought the approach could be used to add DIAL
support to SmartTube. Most people never link using TV code, since it is
more convenient for the auto discovery over WIFI to just work.
That's it. I just wanted to overshare since information about this stuff
is pretty scarce, and it's hard to find.
Cheers!
—
Reply to this email directly, view it on GitHub
<#10>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABM7Z6D65WCHHE7ZI5ODVT3YS6P3JAVCNFSM6AAAAABDC2WTWOVHI2DSMVQWIX3LMV43ASLTON2WKOZSGEZDQNRQGY4TSOI>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
6 tasks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey! I'm the author of Playlet, a YouTube app for Roku TV.
I recently embarked on the journey to support casting from the YouTube app using lounge.
Some projects, like https://github.com/henriquekano/youtube-lounge-api, https://github.com/yuliskov/MediaServiceCore, and (especially) https://github.com/patrickkfkan/yt-cast-receiver helped me understand the protocol better, so I wanted to say thanks, and also share a bit of my findings in case it is useful.
As I tried to understand the DIAL spec, which is the "Link using Wifi" option, I found it was mostly working like this:
Chain of events
M-SEARCH
Notice the
LOCATION
header. This would be a link to an xml file containing the device description.GET
request to theLOCATION
. My TV's response looks something like thisNotice how the service list contains
urn:dial-multiscreen-org:service:dial:1
, which is saying the device is capable of DIAL. Good.In the response headers for hitting the device description
http://IP:8060/dial/dd.xml
, there's one important header to capture:Because it is the URL used to check if a specific DIAL compatible app is installed or not.
GET
request toApplication-Url
+/YouTube
(case sensitive). In this case that would behttp://IP:8060/dial/YouTube
. The response would be something like this:If the mobile device gets a valid response, then it will see the device over wifi and offer to connect to it (it will show up as the
friendlyName
from the device description file.)Note: on my Roku, the request will be blocked, unless the
Origin
header is set tohttps://www.youtube.com
http://IP:8060/dial/YouTube
Might be in different state, and the mobile devices react differently based on the presence of theadditionalData
field.additionalData
(so similar to the content from step 4)POST
request tohttp://IP:8060/dial/YouTube
, which includestheme
andpairingCode
url encoded in the body. The pairing code can be registered usinghttps://www.youtube.com/api/lounge/pairing/register_pairing_code
and then the mobile phone would join the lounge once the code is registered.additionalData
, e.g. :Then the phone will not send a pairing code, and will just join the lounge, since it has everything it needs already.
additionalData
, but not containing aloungeToken
, e.g :Now this is the part I haven't explored yet - but I'm guessing a
passiveSessionId
is some form of new feature allowing the session to be passive until a device connects to it.<options allowStop="true" />
is returned, the mobile device (actually I tested this with Chrome browser) will a send aDELETE
request to close the app. Just a small detail.Implementation
So the device is responsible for responding to the
M-SEARCH
requests, and for providing a device description xml file. So by definition, only one app can have the identifierYouTube
for DIAL. This is why when the YouTube mobile app casts to a device, the official YouTube tv app will launch. This is why I went a different route.A DIAL server
I have a udp connection listening for broadcast messages. When it sees an
M-SEARCH
, it responds just like the device does (see step 2 in the first section), but I set theLOCATION
here to point to a file that I'm serving on a local web server running on the app.Serving device description
Using a local server, I'm serving a "virtual device description" xml file, and also serve an xml file for the
/dial/YouTube
file.For the device description, I set the
friendlyName
to"Playlet on" + tvName
so that users can Identify the app they want to cast to.So far I'm have a working implementation (just a PoC that needs cleanup) here iBicha/playlet#276 that does what I'm describing. I thought the approach could be used to add DIAL support to SmartTube. Most people never link using TV code, since it is more convenient for the auto discovery over WIFI to just work.
That's it. I just wanted to overshare since information about this stuff is pretty scarce, and it's hard to find.
Cheers!
The text was updated successfully, but these errors were encountered: