/// <summary> /// Handles an individual HTTP message /// Does the required action if it is a request, and sends the required response /// If the message is a response from the iOS device it does nothing /// </summary> /// <param name="clientStream">The stream that the message came from</param> /// <param name="message">The message text</param> private void handleMessageReceived(NetworkStream clientStream, string message, byte[] rawData) { message = message.Trim(); if (message.StartsWith("POST /reverse HTTP/1.1")) //initial opening message, declares this connection to the the one that will be two-way { twoWayStream = clientStream; //this is the two way stream for comms use in future. Store it so we can use it for sending playback events later. If we try to use any other connection's stream, the iOS device will ignore it or refuse the connection. string response = "HTTP/1.1 101 Switching Protocols\r\n" + "Date: " + String.Format("{0:R}", DateTime.Now) + "\r\n" + "Upgrade: PTTH/1.0\r\n" + "Connection: Upgrade\r\n" + "\r\n"; sendMessage(clientStream, response); return; } if (message.StartsWith("POST /scrub?position=")) //seek. { //regex to get position Regex regex = new Regex(@"POST /scrub\?position=([0-9\.]+) HTTP/1.1", RegexOptions.Multiline); Match match = regex.Match(message); if (match.Success) { //scrub to position string pos = match.Groups[1].ToString(); playbackEvent(this, "scrub", pos); } //reply with ok message sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("POST /play")) //play. { //get the url out of the message and play it here //URL is immediately after "Content-Location: " string url; string[] array = Regex.Split(message, "Content-Location: "); if (array.Count() > 1) { //split by URL and Start-location array = Regex.Split(array[1], "Start-Position: "); if (array.Count() > 1) { url = array[0].Trim(); Debug.WriteLine("Attempting to play URL" + url); //get the start position double start = 0; Match m = Regex.Match(array[1].Trim(), @"(\d*\.?\d*)"); //sometimes some random stuff comes after the number, just want the number if (m.Success) { try { start = Convert.ToDouble(m.Value); } catch (FormatException) { } } playURL(this, url, start); } } //reply with postion message sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("GET /scrub HTTP/1.1")) //this is a request from the iOS device to ask the application how far along the playback is so it can update its progress bar { //get the current duration Publish theMainWindow = (Publish)Application.OpenForms["Publish"]; int duration = theMainWindow.getPlayerDuration(); int position = theMainWindow.getPlayerPosition(); //get the response data string string responsedata = String.Format("duration: {0:0.000000}\nposition: {1:0.000000}", duration, position); //get the content length and add one for the newline at the end int contentLength = responsedata.Length + 1; //send the current playback position status string response = "HTTP/1.1 200 OK\r\n" + "Date: " + String.Format("{0:R}", DateTime.Now) + "\r\n" + "Content-Length: " + contentLength + "\r\n\r\n" + responsedata + "" + "\n"; sendMessage(clientStream, response); return; } if (message.StartsWith("POST /rate?value=0.000000")) //this is how the iOS device requests the video should be paused { //pause playbackEvent(this, "pause", ""); sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("POST /rate?value=1.000000")) //this is how the iOS device requests the video should be played { //play playbackEvent(this, "play", ""); sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("POST /stop HTTP/1.1")) //stop { //stop the playback playbackEvent(this, "stop", ""); sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("PUT /photo HTTP/1.1")) //photo { //get the position in the rawdata where the image starts int index = message.IndexOf("\r\n\r\n"); index += 4; //the four \r\n\r\n characters MemoryStream ms = new MemoryStream(rawData.Skip(index).ToArray()); Image returnImage = Image.FromStream(ms); playImage(this, returnImage); } if (message.StartsWith("POST /authorize HTTP/1.1")) //attempt to play a DRM track from the device (probably from ipod app, not youtube). Not currently supported as I don't know how to pass the key to quicktime! If anyone knows how to pass it to the quicktime control, LMK. { authorisationRequest(); //notify the GUI that a DRM track was requested playbackEvent(this, "stop"); //we can't deal with the video for now, for the above reason. sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("GET") || message.StartsWith("POST")) //unknown { //still a request of some sort (not a reply to a message sent to device), so reply with ok. sendHTTPOKMessage(clientStream); return; } }
/// <summary> /// Handles an individual HTTP message /// Does the required action if it is a request, and sends the required response /// If the message is a response from the iOS device it does nothing /// </summary> /// <param name="clientStream">The stream that the message came from</param> /// <param name="message">The message text</param> /// //从这里开始处理实际请求 nick private void handleMessageReceived(NetworkStream clientStream, string message, byte[] rawData) { //message = message.Trim(); //Nick ban string[] messageparse = message.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); //Nick parse string cseq = "kong"; string contentlen = "kong"; int type = 0; int seq = 0; foreach (string trystr in messageparse) { if (trystr.Contains("CSeq")) { cseq = parseHeader(trystr, "CSeq: "); } if (trystr.Contains("Content-Length")) { contentlen = parseHeader(trystr, "Content-Length: "); } } Debug.WriteLine("Cseq: " + cseq); Debug.WriteLine("contentlen: " + contentlen); if (message.StartsWith("POST /fp-")) { int httpcontentstart = 0; for (int a = 0; a < 1023; a++) { if (rawData[a] == 0x46 && rawData[a + 1] == 0x50 && rawData[a + 2] == 0x4c && rawData[a + 3] == 0x59) { //Debug.WriteLine(a); type = rawData[a + 5]; seq = rawData[a + 6]; httpcontentstart = a; break; } } int httpcontentlenth = Convert.ToInt32(contentlen); byte[] httpcontent = new byte[httpcontentlenth]; for (int hc = 0; hc < httpcontentlenth; hc++) { httpcontent[hc] = rawData[httpcontentstart + hc]; } Debug.WriteLine("type: " + type); Debug.WriteLine("seq: " + seq); if (type == 1) { if (seq == 1) { string header = "RTSP/1.0 200 OK\r\nContent-Type: application/octet-stream\r\nServer: AirTunes/220.68\r\nContent-Length: 142\r\nCSeq:" + cseq + "\r\n\r\n"; sendMessage(clientStream, header); int sb1 = (int)fairplay_setup(httpcontent, httpcontentlenth); byte[] vb1 = new byte[142]; for (int i = 0; i < 142; i++) { vb1[i] = (byte)Marshal.PtrToStructure((IntPtr)(sb1++), typeof(byte)); //Console.Write((char)vb1[i]); } clientStream.Write(vb1, 0, vb1.Length); clientStream.Flush(); } else if (seq == 3) { string header = "RTSP/1.0 200 OK\r\nContent-Type: application/octet-stream\r\nServer: AirTunes/220.68\r\nContent-Length: 32\r\nCSeq:" + cseq + "\r\n\r\n"; sendMessage(clientStream, header); int sb1 = (int)fairplay_setup(httpcontent, httpcontentlenth); byte[] vb1 = new byte[142]; for (int i = 0; i < 142; i++) { vb1[i] = (byte)Marshal.PtrToStructure((IntPtr)(sb1++), typeof(byte)); //Console.Write((char)vb1[i]); } clientStream.Write(vb1, 0, 32); clientStream.Flush(); } } } if (message.StartsWith("SETUP")) { if (cseq == "2") { string setup1 = "RTSP/1.0 200 OK\r\nContent-Type: application/x-apple-binary-plist\r\nServer: AirTunes/220.68\r\nContent-Length: 0\r\nCseq: " + "2" + "\r\n\r\n"; sendMessage(clientStream, setup1); } } if (message.StartsWith("GET /info")) { string getinfocontentlen = "nick"; Dictionary <string, object> getinforespdict = new Dictionary <string, object> { { "macAddress", "3F:5D:62:2C:41:36" }, //mac地址,需要修改 { "statusFlags", 68 }, //需要修改,原版为0x4 { "pi", "fffffd2d-fe60-42ab-8877-d3ac6084459a" }, { "deviceID", "3F:5D:62:2C:41:36" }, { "keepAliveSendStatsAsBody", true }, { "vv", 2 }, //这个应该不用改,和原始的mdns一样 { "audioLatencies", new Dictionary <string, object>[] { new Dictionary <string, object> { { "type", 100 }, { "inputLatencyMicros", 0 }, { "audioType", "default" }, { "outputLatencyMicros", 0 } }, new Dictionary <string, object> { { "type", 101 }, { "inputLatencyMicros", 0 }, { "audioType", "default" }, { "outputLatencyMicros", 0 } } } }, { "audioFormats", new Dictionary <string, object>[] { new Dictionary <string, object> { { "audioOutputFormats", 67108860 }, { "type", 100 }, { "audioInputFormats", 67108860 } }, new Dictionary <string, object> { { "audioOutputFormats", 67108860 }, { "type", 101 }, { "audioInputFormats", 67108860 } } } }, { "displays", new Dictionary <string, object>[] { new Dictionary <string, object> { { "widthPhysical", 0 }, { "heightPhysical", 0 }, { "uuid", "e5f7a68d-7b0f-4305-984b-974f677a150b" }, { "widthPixels", 1280 }, { "edid", new byte[] { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 , 0x5a, 0x63, 0x22, 0x38, 0x00, 0x00, 0x00, 0x00, 0x31, 0x12, 0x01, 0x03, 0x80, 0x34, 0x1d, 0x78 , 0x2e, 0xee, 0xd5, 0xa5, 0x55, 0x48, 0x9b, 0x26, 0x12, 0x50, 0x54, 0xbf, 0xef, 0x80, 0xd1, 0xc0 , 0xb3, 0x00, 0xa9, 0x40, 0x95, 0x00, 0x90, 0x40, 0x81, 0x80, 0x81, 0x40, 0x71, 0x4f, 0x02, 0x3a , 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x08, 0x22, 0x21, 0x00, 0x00, 0x1e , 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, 0x4b, 0x0f, 0x52, 0x12, 0x00, 0x0a, 0x20, 0x20 , 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x56, 0x58, 0x32, 0x34, 0x33, 0x33, 0x77 , 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x01, 0xf6, 0x02, 0x03, 0x25, 0xf1, 0x52, 0x90, 0x05, 0x04 , 0x03, 0x02, 0x07, 0x06, 0x0f, 0x0e, 0x1f, 0x14, 0x1e, 0x1d, 0x13, 0x12, 0x11, 0x16, 0x01, 0x23 , 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x02, 0x3a, 0x80 , 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x08, 0x22, 0x21, 0x00, 0x00, 0x1e, 0x01 , 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c, 0x25, 0x00, 0x08, 0x22, 0x21, 0x00, 0x00 , 0x9e, 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0x08, 0x22, 0x21 , 0x00, 0x00, 0x1e, 0x02, 0x3a, 0x80, 0xd0, 0x72, 0x38, 0x2d, 0x40, 0x10, 0x2c, 0x45, 0x80, 0x08 , 0x22, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96 , 0x00, 0x08, 0x22, 0x21, 0x00, 0x00, 0x18, 0xe3 } }, { "height", 720 }, { "features", 14 }, { "rotation", true }, { "overscanned", true }, { "refreshRate", 0.016667 }, { "width", 1280 }, { "heightPixels", 720 } } } }, { "pk", new byte[] { 0x23, 0xf3, 0x1f, 0x8f, 0xd4, 0xe5, 0x02, 0xfa, 0xf0, 0xf0, 0x51, 0x89, 0x0b, 0x89, 0x23 , 0xa1, 0xe0, 0xe2, 0x9d, 0xdf, 0xca, 0x1d, 0x46, 0x6b, 0x4d, 0xd0, 0x01, 0xd2, 0xf5, 0xc3, 0x35, 0xe0 } }, //这里的pk目前看来跟mdns的对不上,想不明白 { "model", "AppleTV3,2" }, { "features", 1518338039 }, //这个feature是需要pair-verify的,需要修改 { "name", "Apple TV" }, { "keepAliveLowPower", true }, { "sourceVersion", "220.68" } }; //int[] x = { 2, 3, { 4 } }; //CS0623 string getinfoheader = "RTSP/1.0 200 OK\r\nContent-Type: application/x-apple-binary-plist\r\nServer: AirTunes/220.68\r\nCSeq: " + cseq + "\r\nContent-Length: " + getinfocontentlen + "\r\n\r\n"; } if (message.StartsWith("POST /reverse HTTP/1.1")) //initial opening message, declares this connection to the the one that will be two-way { twoWayStream = clientStream; //this is the two way stream for comms use in future. Store it so we can use it for sending playback events later. If we try to use any other connection's stream, the iOS device will ignore it or refuse the connection. string response = "HTTP/1.1 101 Switching Protocols\r\n" + "Date: " + String.Format("{0:R}", DateTime.Now) + "\r\n" + "Upgrade: PTTH/1.0\r\n" + "Connection: Upgrade\r\n" + "\r\n"; sendMessage(clientStream, response); return; } if (message.StartsWith("POST /scrub?position=")) //seek. { //regex to get position Regex regex = new Regex(@"POST /scrub\?position=([0-9\.]+) HTTP/1.1", RegexOptions.Multiline); Match match = regex.Match(message); if (match.Success) { //scrub to position string pos = match.Groups[1].ToString(); playbackEvent(this, "scrub", pos); } //reply with ok message sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("POST /play")) //play. { //get the url out of the message and play it here //URL is immediately after "Content-Location: " string url; string[] array = Regex.Split(message, "Content-Location: "); if (array.Count() > 1) { //split by URL and Start-location array = Regex.Split(array[1], "Start-Position: "); if (array.Count() > 1) { url = array[0].Trim(); Debug.WriteLine("Attempting to play URL" + url); //get the start position double start = 0; Match m = Regex.Match(array[1].Trim(), @"(\d*\.?\d*)"); //sometimes some random stuff comes after the number, just want the number if (m.Success) { try { start = Convert.ToDouble(m.Value); } catch (FormatException) { } } playURL(this, url, start); } } //reply with postion message sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("GET /scrub HTTP/1.1")) //this is a request from the iOS device to ask the application how far along the playback is so it can update its progress bar { //get the current duration Publish theMainWindow = (Publish)Application.OpenForms["Publish"]; int duration = theMainWindow.getPlayerDuration(); int position = theMainWindow.getPlayerPosition(); //get the response data string string responsedata = String.Format("duration: {0:0.000000}\nposition: {1:0.000000}", duration, position); //get the content length and add one for the newline at the end int contentLength = responsedata.Length + 1; //send the current playback position status string response = "HTTP/1.1 200 OK\r\n" + "Date: " + String.Format("{0:R}", DateTime.Now) + "\r\n" + "Content-Length: " + contentLength + "\r\n\r\n" + responsedata + "" + "\n"; sendMessage(clientStream, response); return; } if (message.StartsWith("POST /rate?value=0.000000")) //this is how the iOS device requests the video should be paused { //pause playbackEvent(this, "pause", ""); sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("POST /rate?value=1.000000")) //this is how the iOS device requests the video should be played { //play playbackEvent(this, "play", ""); sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("POST /stop HTTP/1.1")) //stop { //stop the playback playbackEvent(this, "stop", ""); sendHTTPOKMessage(clientStream); return; } if (message.StartsWith("PUT /photo HTTP/1.1")) //photo { //get the position in the rawdata where the image starts int index = message.IndexOf("\r\n\r\n"); index += 4; //the four \r\n\r\n characters MemoryStream ms = new MemoryStream(rawData.Skip(index).ToArray()); Image returnImage = Image.FromStream(ms); playImage(this, returnImage); } if (message.StartsWith("POST /authorize HTTP/1.1")) //attempt to play a DRM track from the device (probably from ipod app, not youtube). Not currently supported as I don't know how to pass the key to quicktime! If anyone knows how to pass it to the quicktime control, LMK. { authorisationRequest(); //notify the GUI that a DRM track was requested playbackEvent(this, "stop"); //we can't deal with the video for now, for the above reason. sendHTTPOKMessage(clientStream); return; } /* * if (message.StartsWith("GET") || message.StartsWith("POST")) //unknown * { * //still a request of some sort (not a reply to a message sent to device), so reply with ok. * sendHTTPOKMessage(clientStream); * return; * } */ }