ConnectSDK / Connect-SDK-iOS

iOS source project for Connect SDK
Apache License 2.0
176 stars 139 forks source link

AppleTV3,1 'POST Photo' protocol Not Working anymore #157

Closed loretoparisi closed 9 years ago

loretoparisi commented 9 years ago

It seems that on AppleTV3,1 devices:

{
deviceid = "xxxxx";
features = "0x5A7FFFF7,0xE";
flags = 0x44;
model = "AppleTV3,1";
pi = "43f884bc-eb56-459d-a307-9cce844f86bf";
pk = 8b197524a8f78819b8e64b8d8cd4a70faec1b49317e517fb81f624628a313a39;
srcvers = "220.68";
vv = 2;
}

The 'Photo' protocol is not working anymore. Media Control for "Video" and "Audio" works.

I's easy to test it. Just use the "Connect SDK Sampler" app against a AppleTV3,1 device and send a photo then. The app will log an error:

2015-05-22 17:10:38.286 ConnectSDKSampler[8926:824787] display photo failure: A generic error occured

I get the same error using the old AirPlayKit ( https://github.com/rothacr/AirplayKit/issues/4 ), so it seems to be a Async Socket error from the AppleTV side to me.

The 'photo' protocol works fine with AirPlay compatible devices like "AirServer" ( http://www.airserver.com/ ).

Also, the "photo" protocol, works fine on AppleTV3,1 with Erica Sadun's AirFlick utility ( rica Sadun's AirFlick ). This seems very weird, since I assume this app is not updated from a while.

I add here that sending a photo image on Apple TV is sending a socket message like

NSData *imageData = haRetinaDisplay ? UIImageJPEGRepresentation(image, _imageQuality) : UIImagePNGRepresentation(image);
        NSUInteger length = [imageData length];
        NSString *message = [[NSString alloc] initWithFormat:@"PUT /photo HTTP/1.1\r\n"
                             "Content-Length: %@\r\n"
                             "Host: musiXmatch\r\n"
                             "User-Agent: MediaControl/1.0\r\n\r\n", @(length)];
        NSMutableData *messageData = [[NSMutableData alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding]];
        [messageData appendData:imageData];

with the image is of mime type jpeg or png.

Regarding ConnectSDK, the classes involved are AirPlayServiceHTTP.m and AirPlayServer.m

The image is sent by class method:

- (void) displayImageWithMediaInfo:(MediaInfo *)mediaInfo success:(MediaPlayerSuccessBlock)success failure:(FailureBlock)failure

The error that is trapped is a ConnectError and it has a status code of type ConnectStatusCode that is not in the enum, so then you get here

default:
            if (details)
                errorMessage = [NSString stringWithFormat:@"A generic error occured: %@", details];
            else
                errorMessage = [NSString stringWithFormat:@"A generic error occured"];

in class method

+ (NSError *) generateErrorWithCode:(ConnectStatusCode)code andDetails:(id)details

This error is raised by AirPlayServiceHTTP.m in method:

- (int) sendCommand:(ServiceCommand *)command withPayload:(id)payload toURL:(NSURL *)URL

due to a HTTP response code not 200:

CTASIHTTPRequest *strongRequest = weakRequest;

        if (strongRequest.responseStatusCode == 200)
        {
//....OK
} else
        {
            if (command.callbackError)
                dispatch_on_main(^{ command.callbackError([ConnectError generateErrorWithCode:strongRequest.responseStatusCode andDetails:nil]); });
        }

where the HTTP Response status was 404 / Not Found.

Here are more debugging info about this request to AirServer

(lldb) po strongRequest.responseStatusCode
404
(lldb) po strongRequest.responseHeaders
{
    "Content-Length" = 0;
    Server = "AirTunes/220.68";
}
(lldb) po strongRequest.responseStatusMessage
HTTP/1.1 404 Not Found
loretoparisi commented 9 years ago

Adding more info about AppleTV3,1 here:

macbookproloreto:TherePlay-master admin$ curl --verbose "http://192.168.2.112:7000/server-info"
* Hostname was NOT found in DNS cache
*   Trying 192.168.2.112...
* Connected to 192.168.2.112 (192.168.2.112) port 7000 (#0)
> GET /server-info HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 192.168.2.112:7000
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 22 May 2015 15:50:58 GMT
< Content-Length: 581
< Content-Type: text/x-apple-plist+xml
< 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>deviceid</key>
    <string>9C:20:7B:E6:70:18</string>
    <key>features</key>
    <integer>61647880183</integer>
    <key>macAddress</key>
    <string>xxxxxx</string>
    <key>model</key>
    <string>AppleTV3,1</string>
    <key>osBuildVersion</key>
    <string>12F69</string>
    <key>protovers</key>
    <string>1.0</string>
    <key>srcvers</key>
    <string>220.68</string>
    <key>vv</key>
    <integer>2</integer>
</dict>
</plist>
* Connection #0 to host 192.168.2.112 left intact
loretoparisi commented 9 years ago

If I try to send the image using cURL, I can see a Black Screen on the TV, and that is what I get from AppleTV3,1

macbookproloreto:TherePlay-master admin$ curl --verbose -X PUT "http://192.168.2.112:7000/photo" --header "User-Agent: MediaControl/1.0" --header "X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c" --header "X-Apple-AssetKey: F92F9B91-954E-4D63-BB9A-EEC771ADE6E8" --header "Content-Type: image/jpeg" --data-binary "@/Users/admin/Desktop/SPACE.JPG"
* Hostname was NOT found in DNS cache
*   Trying 192.168.2.112...
* Connected to 192.168.2.112 (192.168.2.112) port 7000 (#0)
> PUT /photo HTTP/1.1
> Host: 192.168.2.112:7000
> Accept: */*
> User-Agent: MediaControl/1.0
> X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c
> X-Apple-AssetKey: F92F9B91-954E-4D63-BB9A-EEC771ADE6E8
> Content-Type: image/jpeg
> Content-Length: 61079
> Expect: 100-continue
> 
* Done waiting for 100-continue
< HTTP/1.1 200 OK
< Date: Fri, 22 May 2015 16:17:47 GMT
< Content-Length: 0
< 
* Connection #0 to host 192.168.2.112 left intact

Look at the cURL request:

curl --verbose -X PUT "http://192.168.2.112:7000/photo" \
 --header "User-Agent: MediaControl/1.0" \
--header "X-Apple-Session-ID: 1bd6ceeb-fffd-456c-a09c-996053a7a08c" \
--header "X-Apple-AssetKey: F92F9B91-954E-4D63-BB9A-EEC771ADE6E8" \
--header "Content-Type: image/jpeg" \
--data-binary "@/Users/admin/Desktop/SPACE.JPG"

The Content-Length header should not be necessary, anyways, nothing better happens with:

--header 'Content-Length: 61079'

loretoparisi commented 9 years ago

I found out that Open Airplay PHP library works fine: https://github.com/jamesdlow/open-airplay

php airplay.php -h 192.168.2.112:7000 -p /Users/admin/Desktop/SPACE.JPG

but the Shell example has the same issue that mine ( https://github.com/jamesdlow/open-airplay/issues/8 )

curl -D test.txt -X PUT --data-binary @$filename -H 'Connection: keep-alive' -H 'User-Agent: MediaControl/1.0' -H 'X-Apple-Transition: None' -H 'Content-Type:' -H 'Accept:' -H 'Host:' -H 'Expect:' http://192.168.2.112:7000/photo

The result is a black screen /disconnect.

For some reason the PHP library works fine: https://github.com/jamesdlow/open-airplay/blob/master/PHP/airplay.php

eunikolsky commented 9 years ago

@loretoparisi, thanks for investigation and comments! We'll try to reproduce and fix it.

loretoparisi commented 9 years ago

Any news on this issue?

eunikolsky commented 9 years ago

I'm testing this on AppleTV3,2 (A1469), OS 6.0.2 (6646.81.1). Works fine. I'll try to upgrade the OS if possible.

eunikolsky commented 9 years ago

I can reproduce it now on OS 7.2 (7512). @loretoparisi, do you have any ideas of what could be broken?

eunikolsky commented 9 years ago

OK, the problem is that ConnectSDK uses the POST method and open-airplay uses PUT, which is correct. I'll need to check it on Apple TV 2 too.

eunikolsky commented 9 years ago

Fixed in https://github.com/ConnectSDK/Connect-SDK-iOS/commit/e05bfb5b3385863eafc10791f51535a2c0a52acc and https://github.com/ConnectSDK/Connect-SDK-iOS-Lite/commit/5916ff397dec250a6c5c878f2db36a325686065c. Merged into dev.

@loretoparisi, thanks for the bug report.

eunikolsky commented 9 years ago

@loretoparisi, please confirm the fix.

loretoparisi commented 9 years ago

thank you @eunikolsky going to check it out!

loretoparisi commented 9 years ago

So I have tried the Sampler App with the dev branch. I get this error:

2015-06-07 19:47:26.297 ConnectSDKSampler[8680:874431] display photo failure: A generic error occured: STATUS CODE 404 MESSAGE:HTTP/1.1 404 Not Found HEADERS:{ "Content-Length" = 0; Server = "AirTunes/220.68"; }

The 404 error was not related to the image url that it works: http://ec2-54-201-108-205.us-west-2.compute.amazonaws.com/samples/media/photo.jpg, so I assume it is the protocol as you stated.

In fact it was the POST method instead of PUT in the AirPlayServiceHTTP.m method

- (void) displayImageWithMediaInfo:(MediaInfo *)mediaInfo success:(MediaPlayerSuccessBlock)success failure:(FailureBlock)failure
{
    _assetId = [[CTGuid randomGuid] stringValue];

    NSString *commandPathComponent = @"photo";
    NSURL *commandURL = [self.service.serviceDescription.commandURL URLByAppendingPathComponent:commandPathComponent];

    ServiceCommand *command = [ServiceCommand commandWithDelegate:self target:commandURL payload:nil];
    command.HTTPMethod = @"POST"; // Here with PUT it works :)

Have you merged that file as well?

That said, I was not able to make it working anyways through the cURL shell command and PUT HTTP method, but if this could be something different or related to that issue I cannot say.

curl --request PUT --data-binary @"/Users/admin/Desktop/SPACE.JPG" -H 'Content-Type: image/jpeg' -H 'Connection: Keep-Alive' -H 'Content-Length:61079' -H 'X-Apple-AssetKey: 3933d7e9-b340-4a9a-a2cb-e11b4294cefd61079' -H 'X-Apple-Session-ID: b4fa3c4b-749f-4520-9795-84e978fbe95b' http://10.0.1.2:7000/photo

eunikolsky commented 9 years ago

@loretoparisi, have you updated the core submodule? The current dev branch points to commit https://github.com/ConnectSDK/Connect-SDK-iOS-Core/commit/3385d165e537d66c45001ebc503d5892bffc241a, where this is fixed.

loretoparisi commented 9 years ago

Yes, that was. I think we can close this issue. Even if I'm not sure of the related issues, but those ones are related to other projects. Keep going the good work!