mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
raop : whitespace cleanup
This commit is contained in:
@@ -60,9 +60,9 @@ raop_handler_info(raop_conn_t *conn,
|
||||
printf("qualifier: %s\n", qualifier_string);
|
||||
txtAirPlay = true;
|
||||
}
|
||||
if (qualifier_string) {
|
||||
if (qualifier_string) {
|
||||
free(qualifier_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
plist_t res_node = plist_new_dict();
|
||||
@@ -265,9 +265,9 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
|
||||
if (PLIST_IS_STRING(req_method_node) && PLIST_IS_STRING(req_user_node)) {
|
||||
/* this is the initial pair-setup-pin request */
|
||||
const char *salt;
|
||||
char pin[6];
|
||||
const char *pk;
|
||||
int len_pk, len_salt;
|
||||
char pin[6];
|
||||
const char *pk;
|
||||
int len_pk, len_salt;
|
||||
char *method = NULL;
|
||||
char *user = NULL;
|
||||
plist_get_string_val(req_method_node, &method);
|
||||
@@ -280,13 +280,13 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
|
||||
return;
|
||||
}
|
||||
free (method);
|
||||
plist_get_string_val(req_user_node, &user);
|
||||
plist_get_string_val(req_user_node, &user);
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "pair-setup-pin: device_id = %s", user);
|
||||
snprintf(pin, 6, "%04u", conn->raop->pin % 10000);
|
||||
if (conn->raop->pin < 10000) {
|
||||
conn->raop->pin = 0;
|
||||
}
|
||||
int ret = srp_new_user(conn->session, conn->raop->pairing, (const char *) user,
|
||||
int ret = srp_new_user(conn->session, conn->raop->pairing, (const char *) user,
|
||||
(const char *) pin, &salt, &len_salt, &pk, &len_pk);
|
||||
free(user);
|
||||
plist_free(req_root_node);
|
||||
@@ -302,19 +302,19 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
|
||||
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
|
||||
plist_free(res_root_node);
|
||||
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
|
||||
return;
|
||||
return;
|
||||
} else if (PLIST_IS_DATA(req_pk_node) && PLIST_IS_DATA(req_proof_node)) {
|
||||
/* this is the second part of pair-setup-pin request */
|
||||
char *client_pk = NULL;
|
||||
char *client_proof = NULL;
|
||||
unsigned char proof[64];
|
||||
memset(proof, 0, sizeof(proof));
|
||||
unsigned char proof[64];
|
||||
memset(proof, 0, sizeof(proof));
|
||||
uint64_t client_pk_len;
|
||||
uint64_t client_proof_len;
|
||||
plist_get_data_val(req_pk_node, &client_pk, &client_pk_len);
|
||||
plist_get_data_val(req_proof_node, &client_proof, &client_proof_len);
|
||||
if (logger_debug) {
|
||||
char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20);
|
||||
char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "client SRP6a proof <M> :\n%s", str);
|
||||
free (str);
|
||||
}
|
||||
@@ -339,7 +339,7 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
|
||||
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
|
||||
plist_free(res_root_node);
|
||||
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
|
||||
return;
|
||||
return;
|
||||
} else if (PLIST_IS_DATA(req_epk_node) && PLIST_IS_DATA(req_authtag_node)) {
|
||||
/* this is the third part of pair-setup-pin request */
|
||||
char *client_epk = NULL;
|
||||
@@ -347,25 +347,25 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
|
||||
uint64_t client_epk_len;
|
||||
uint64_t client_authtag_len;
|
||||
unsigned char epk[ED25519_KEY_SIZE];
|
||||
unsigned char authtag[GCM_AUTHTAG_SIZE];
|
||||
int ret;
|
||||
unsigned char authtag[GCM_AUTHTAG_SIZE];
|
||||
int ret;
|
||||
plist_get_data_val(req_epk_node, &client_epk, &client_epk_len);
|
||||
plist_get_data_val(req_authtag_node, &client_authtag, &client_authtag_len);
|
||||
|
||||
if (logger_debug) {
|
||||
if (logger_debug) {
|
||||
char *str = utils_data_to_string((const unsigned char *) client_epk, client_epk_len, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "client_epk %d:\n%s\n", (int) client_epk_len, str);
|
||||
str = utils_data_to_string((const unsigned char *) client_authtag, client_authtag_len, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "client_authtag %d:\n%s\n", (int) client_authtag_len, str);
|
||||
free (str);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(epk, client_epk, ED25519_KEY_SIZE);
|
||||
memcpy(authtag, client_authtag, GCM_AUTHTAG_SIZE);
|
||||
memcpy(epk, client_epk, ED25519_KEY_SIZE);
|
||||
memcpy(authtag, client_authtag, GCM_AUTHTAG_SIZE);
|
||||
free (client_authtag);
|
||||
free (client_epk);
|
||||
plist_free(req_root_node);
|
||||
ret = srp_confirm_pair_setup(conn->session, conn->raop->pairing, epk, authtag);
|
||||
ret = srp_confirm_pair_setup(conn->session, conn->raop->pairing, epk, authtag);
|
||||
if (ret < 0) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n");
|
||||
goto authentication_failed;
|
||||
@@ -375,13 +375,13 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
|
||||
pairing_session_set_setup_status(conn->session);
|
||||
plist_t res_root_node = plist_new_dict();
|
||||
plist_t res_epk_node = plist_new_data((const char *) epk, 32);
|
||||
plist_t res_authtag_node = plist_new_data((const char *) authtag, 16);
|
||||
plist_t res_authtag_node = plist_new_data((const char *) authtag, 16);
|
||||
plist_dict_set_item(res_root_node, "epk", res_epk_node);
|
||||
plist_dict_set_item(res_root_node, "authTag", res_authtag_node);
|
||||
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
|
||||
plist_free(res_root_node);
|
||||
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
authentication_failed:;
|
||||
http_response_init(response, "RTSP/1.0", 470, "Client Authentication Failure");
|
||||
@@ -439,58 +439,58 @@ raop_handler_pairverify(raop_conn_t *conn,
|
||||
return;
|
||||
}
|
||||
switch (data[0]) {
|
||||
case 1:
|
||||
if (datalen != 4 + X25519_KEY_SIZE + ED25519_KEY_SIZE) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
}
|
||||
/* We can fall through these errors, the result will just be garbage... */
|
||||
if (pairing_session_handshake(conn->session, data + 4, data + 4 + X25519_KEY_SIZE)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake");
|
||||
}
|
||||
if (pairing_session_get_public_key(conn->session, public_key)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key");
|
||||
}
|
||||
if (pairing_session_get_signature(conn->session, signature)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature");
|
||||
}
|
||||
if (register_check) {
|
||||
bool registered_client = true;
|
||||
if (conn->raop->callbacks.check_register) {
|
||||
const unsigned char *pk = data + 4 + X25519_KEY_SIZE;
|
||||
char *pk64;
|
||||
ed25519_pk_to_base64(pk, &pk64);
|
||||
registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk64);
|
||||
free (pk64);
|
||||
}
|
||||
|
||||
if (!registered_client) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
*response_data = malloc(sizeof(public_key) + sizeof(signature));
|
||||
if (*response_data) {
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
memcpy(*response_data, public_key, sizeof(public_key));
|
||||
memcpy(*response_data + sizeof(public_key), signature, sizeof(signature));
|
||||
*response_datalen = sizeof(public_key) + sizeof(signature);
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "2nd pair-verify step: checking signature");
|
||||
if (datalen != 4 + PAIRING_SIG_SIZE) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
case 1:
|
||||
if (datalen != 4 + X25519_KEY_SIZE + ED25519_KEY_SIZE) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
}
|
||||
/* We can fall through these errors, the result will just be garbage... */
|
||||
if (pairing_session_handshake(conn->session, data + 4, data + 4 + X25519_KEY_SIZE)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake");
|
||||
}
|
||||
if (pairing_session_get_public_key(conn->session, public_key)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key");
|
||||
}
|
||||
if (pairing_session_get_signature(conn->session, signature)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature");
|
||||
}
|
||||
if (register_check) {
|
||||
bool registered_client = true;
|
||||
if (conn->raop->callbacks.check_register) {
|
||||
const unsigned char *pk = data + 4 + X25519_KEY_SIZE;
|
||||
char *pk64;
|
||||
ed25519_pk_to_base64(pk, &pk64);
|
||||
registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk64);
|
||||
free (pk64);
|
||||
}
|
||||
|
||||
if (pairing_session_finish(conn->session, data + 4)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature");
|
||||
http_response_set_disconnect(response, 1);
|
||||
if (!registered_client) {
|
||||
return;
|
||||
}
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-verify: signature is verified");
|
||||
}
|
||||
*response_data = malloc(sizeof(public_key) + sizeof(signature));
|
||||
if (*response_data) {
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
break;
|
||||
memcpy(*response_data, public_key, sizeof(public_key));
|
||||
memcpy(*response_data + sizeof(public_key), signature, sizeof(signature));
|
||||
*response_datalen = sizeof(public_key) + sizeof(signature);
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "2nd pair-verify step: checking signature");
|
||||
if (datalen != 4 + PAIRING_SIG_SIZE) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pairing_session_finish(conn->session, data + 4)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature");
|
||||
http_response_set_disconnect(response, 1);
|
||||
return;
|
||||
}
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-verify: signature is verified");
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,7 +618,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Failed to generate random pin");
|
||||
pin_4 = 1234;
|
||||
}
|
||||
conn->raop->random_pw = (char *) malloc(pin_len + 1 + 18);
|
||||
conn->raop->random_pw = (char *) malloc(pin_len + 1 + 18);
|
||||
char *pin = conn->raop->random_pw;
|
||||
snprintf(pin, pin_len + 1, "%04u", pin_4 % 10000);
|
||||
pin[pin_len] = '\0';
|
||||
@@ -661,7 +661,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
if (conn->authenticated && conn->raop->random_pw) {
|
||||
free (conn->raop->random_pw);
|
||||
conn->raop->random_pw = NULL;
|
||||
}
|
||||
}
|
||||
if (conn->raop->nonce) {
|
||||
free(conn->raop->nonce);
|
||||
conn->raop->nonce = NULL;
|
||||
@@ -691,17 +691,17 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
|
||||
char* eiv = NULL;
|
||||
uint64_t eiv_len = 0;
|
||||
char *model = NULL;
|
||||
char *model = NULL;
|
||||
char *name = NULL;
|
||||
bool admit_client = true;
|
||||
plist_t req_model_node = plist_dict_get_item(req_root_node, "model");
|
||||
plist_get_string_val(req_model_node, &model);
|
||||
plist_t req_name_node = plist_dict_get_item(req_root_node, "name");
|
||||
plist_get_string_val(req_name_node, &name);
|
||||
if (conn->raop->callbacks.report_client_request) {
|
||||
if (conn->raop->callbacks.report_client_request) {
|
||||
conn->raop->callbacks.report_client_request(conn->raop->callbacks.cls, deviceID, model, name, &admit_client);
|
||||
}
|
||||
if (admit_client && deviceID && name && conn->raop->callbacks.register_client) {
|
||||
if (admit_client && deviceID && name && conn->raop->callbacks.register_client) {
|
||||
char *client_device_id = NULL;
|
||||
char *client_pk = NULL; /* encoded as null-terminated base64 string, must be freed*/
|
||||
get_pairing_session_client_data(conn->session, &client_device_id, &client_pk);
|
||||
@@ -709,7 +709,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk, name);
|
||||
free (client_pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deviceID) {
|
||||
free (deviceID);
|
||||
deviceID = NULL;
|
||||
@@ -727,17 +727,17 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
plist_free(res_root_node);
|
||||
plist_free(req_root_node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
plist_get_data_val(req_eiv_node, &eiv, &eiv_len);
|
||||
memcpy(aesiv, eiv, 16);
|
||||
free(eiv);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len);
|
||||
if (logger_debug) {
|
||||
if (logger_debug) {
|
||||
char* str = utils_data_to_string(aesiv, 16, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
|
||||
char* ekey = NULL;
|
||||
uint64_t ekey_len = 0;
|
||||
@@ -746,7 +746,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
free(ekey);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len);
|
||||
// eaeskey is 72 bytes, aeskey is 16 bytes
|
||||
if (logger_debug) {
|
||||
if (logger_debug) {
|
||||
char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str);
|
||||
free (str);
|
||||
@@ -814,9 +814,9 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Client specified AirPlay2 \"Remote Control\" protocol\n"
|
||||
" Only AirPlay v1 protocol (using NTP and timing port) is supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
char *timing_protocol = NULL;
|
||||
timing_protocol_t time_protocol = TP_NONE;
|
||||
timing_protocol_t time_protocol = TP_NONE;
|
||||
plist_t req_timing_protocol_node = plist_dict_get_item(req_root_node, "timingProtocol");
|
||||
plist_get_string_val(req_timing_protocol_node, &timing_protocol);
|
||||
if (timing_protocol) {
|
||||
@@ -841,7 +841,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
}
|
||||
uint64_t timing_rport = 0;
|
||||
plist_t req_timing_port_node = plist_dict_get_item(req_root_node, "timingPort");
|
||||
if (req_timing_port_node) {
|
||||
if (req_timing_port_node) {
|
||||
plist_get_uint_val(req_timing_port_node, &timing_rport);
|
||||
}
|
||||
if (timing_rport) {
|
||||
@@ -897,106 +897,107 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "type = %llu", type);
|
||||
|
||||
switch (type) {
|
||||
case 110: {
|
||||
// Mirroring
|
||||
unsigned short dport = conn->raop->mirror_data_lport;
|
||||
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
|
||||
uint64_t stream_connection_id = 0;
|
||||
plist_get_uint_val(stream_id_node, &stream_connection_id);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption"
|
||||
" key and iv): %llu", stream_connection_id);
|
||||
case 110: {
|
||||
// Mirroring
|
||||
unsigned short dport = conn->raop->mirror_data_lport;
|
||||
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
|
||||
uint64_t stream_connection_id = 0;
|
||||
plist_get_uint_val(stream_id_node, &stream_connection_id);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption"
|
||||
" key and iv): %llu", stream_connection_id);
|
||||
|
||||
if (conn->raop_rtp_mirror) {
|
||||
raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id);
|
||||
raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
|
||||
http_response_set_disconnect(response, 1);
|
||||
}
|
||||
|
||||
plist_t res_stream_node = plist_new_dict();
|
||||
plist_t res_stream_data_port_node = plist_new_uint(dport);
|
||||
plist_t res_stream_type_node = plist_new_uint(110);
|
||||
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
|
||||
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
|
||||
plist_array_append_item(res_streams_node, res_stream_node);
|
||||
|
||||
break;
|
||||
} case 96: {
|
||||
// Audio
|
||||
unsigned short cport = conn->raop->control_lport, dport = conn->raop->data_lport;
|
||||
unsigned short remote_cport = 0;
|
||||
unsigned char ct = 0;
|
||||
unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
|
||||
|
||||
uint64_t uint_val = 0;
|
||||
plist_t req_stream_control_port_node = plist_dict_get_item(req_stream_node, "controlPort");
|
||||
plist_get_uint_val(req_stream_control_port_node, &uint_val);
|
||||
remote_cport = (unsigned short) uint_val; /* must != 0 to activate audio resend requests */
|
||||
|
||||
plist_t req_stream_ct_node = plist_dict_get_item(req_stream_node, "ct");
|
||||
plist_get_uint_val(req_stream_ct_node, &uint_val);
|
||||
ct = (unsigned char) uint_val;
|
||||
|
||||
if (conn->raop->callbacks.audio_get_format) {
|
||||
/* get additional audio format parameters */
|
||||
uint64_t audioFormat = 0;
|
||||
unsigned short spf = 0;
|
||||
bool isMedia = false;
|
||||
bool usingScreen = false;
|
||||
uint8_t bool_val = 0;
|
||||
|
||||
plist_t req_stream_spf_node = plist_dict_get_item(req_stream_node, "spf");
|
||||
plist_get_uint_val(req_stream_spf_node, &uint_val);
|
||||
spf = (unsigned short) uint_val;
|
||||
|
||||
plist_t req_stream_audio_format_node = plist_dict_get_item(req_stream_node, "audioFormat");
|
||||
plist_get_uint_val(req_stream_audio_format_node, &audioFormat);
|
||||
|
||||
plist_t req_stream_is_media_node = plist_dict_get_item(req_stream_node, "isMedia");
|
||||
if (req_stream_is_media_node) {
|
||||
plist_get_bool_val(req_stream_is_media_node, &bool_val);
|
||||
isMedia = (bool) bool_val;
|
||||
} else {
|
||||
isMedia = false;
|
||||
}
|
||||
|
||||
plist_t req_stream_using_screen_node = plist_dict_get_item(req_stream_node, "usingScreen");
|
||||
if (req_stream_using_screen_node) {
|
||||
plist_get_bool_val(req_stream_using_screen_node, &bool_val);
|
||||
usingScreen = (bool) bool_val;
|
||||
} else {
|
||||
usingScreen = false;
|
||||
}
|
||||
|
||||
conn->raop->callbacks.audio_get_format(conn->raop->callbacks.cls, &ct, &spf, &usingScreen, &isMedia, &audioFormat);
|
||||
}
|
||||
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_start_audio(conn->raop_rtp, &remote_cport, &cport, &dport, &ct, &sr);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success");
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
|
||||
http_response_set_disconnect(response, 1);
|
||||
}
|
||||
|
||||
plist_t res_stream_node = plist_new_dict();
|
||||
plist_t res_stream_data_port_node = plist_new_uint(dport);
|
||||
plist_t res_stream_control_port_node = plist_new_uint(cport);
|
||||
plist_t res_stream_type_node = plist_new_uint(96);
|
||||
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
|
||||
plist_dict_set_item(res_stream_node, "controlPort", res_stream_control_port_node);
|
||||
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
|
||||
plist_array_append_item(res_streams_node, res_stream_node);
|
||||
|
||||
break;
|
||||
if (conn->raop_rtp_mirror) {
|
||||
raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id);
|
||||
raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
|
||||
http_response_set_disconnect(response, 1);
|
||||
}
|
||||
|
||||
default:
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type);
|
||||
plist_t res_stream_node = plist_new_dict();
|
||||
plist_t res_stream_data_port_node = plist_new_uint(dport);
|
||||
plist_t res_stream_type_node = plist_new_uint(110);
|
||||
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
|
||||
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
|
||||
plist_array_append_item(res_streams_node, res_stream_node);
|
||||
|
||||
break;
|
||||
}
|
||||
case 96: {
|
||||
// Audio
|
||||
unsigned short cport = conn->raop->control_lport, dport = conn->raop->data_lport;
|
||||
unsigned short remote_cport = 0;
|
||||
unsigned char ct = 0;
|
||||
unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
|
||||
|
||||
uint64_t uint_val = 0;
|
||||
plist_t req_stream_control_port_node = plist_dict_get_item(req_stream_node, "controlPort");
|
||||
plist_get_uint_val(req_stream_control_port_node, &uint_val);
|
||||
remote_cport = (unsigned short) uint_val; /* must != 0 to activate audio resend requests */
|
||||
|
||||
plist_t req_stream_ct_node = plist_dict_get_item(req_stream_node, "ct");
|
||||
plist_get_uint_val(req_stream_ct_node, &uint_val);
|
||||
ct = (unsigned char) uint_val;
|
||||
|
||||
if (conn->raop->callbacks.audio_get_format) {
|
||||
/* get additional audio format parameters */
|
||||
uint64_t audioFormat = 0;
|
||||
unsigned short spf = 0;
|
||||
bool isMedia = false;
|
||||
bool usingScreen = false;
|
||||
uint8_t bool_val = 0;
|
||||
|
||||
plist_t req_stream_spf_node = plist_dict_get_item(req_stream_node, "spf");
|
||||
plist_get_uint_val(req_stream_spf_node, &uint_val);
|
||||
spf = (unsigned short) uint_val;
|
||||
|
||||
plist_t req_stream_audio_format_node = plist_dict_get_item(req_stream_node, "audioFormat");
|
||||
plist_get_uint_val(req_stream_audio_format_node, &audioFormat);
|
||||
|
||||
plist_t req_stream_is_media_node = plist_dict_get_item(req_stream_node, "isMedia");
|
||||
if (req_stream_is_media_node) {
|
||||
plist_get_bool_val(req_stream_is_media_node, &bool_val);
|
||||
isMedia = (bool) bool_val;
|
||||
} else {
|
||||
isMedia = false;
|
||||
}
|
||||
|
||||
plist_t req_stream_using_screen_node = plist_dict_get_item(req_stream_node, "usingScreen");
|
||||
if (req_stream_using_screen_node) {
|
||||
plist_get_bool_val(req_stream_using_screen_node, &bool_val);
|
||||
usingScreen = (bool) bool_val;
|
||||
} else {
|
||||
usingScreen = false;
|
||||
}
|
||||
|
||||
conn->raop->callbacks.audio_get_format(conn->raop->callbacks.cls, &ct, &spf, &usingScreen, &isMedia, &audioFormat);
|
||||
}
|
||||
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_start_audio(conn->raop_rtp, &remote_cport, &cport, &dport, &ct, &sr);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success");
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
|
||||
http_response_set_disconnect(response, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
plist_t res_stream_node = plist_new_dict();
|
||||
plist_t res_stream_data_port_node = plist_new_uint(dport);
|
||||
plist_t res_stream_control_port_node = plist_new_uint(cport);
|
||||
plist_t res_stream_type_node = plist_new_uint(96);
|
||||
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
|
||||
plist_dict_set_item(res_stream_node, "controlPort", res_stream_control_port_node);
|
||||
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
|
||||
plist_array_append_item(res_streams_node, res_stream_node);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type);
|
||||
http_response_set_disconnect(response, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1036,7 +1037,7 @@ raop_handler_get_parameter(raop_conn_t *conn,
|
||||
char volume[25] = "volume: 0.0\r\n";
|
||||
if (conn->raop->callbacks.audio_set_client_volume) {
|
||||
snprintf(volume, 25, "volume: %9.6f\r\n", conn->raop->callbacks.audio_set_client_volume(conn->raop->callbacks.cls));
|
||||
}
|
||||
}
|
||||
http_response_add_header(response, "Content-Type", "text/parameters");
|
||||
*response_data = strdup(volume);
|
||||
if (*response_data) {
|
||||
@@ -1045,9 +1046,11 @@ raop_handler_get_parameter(raop_conn_t *conn,
|
||||
return;
|
||||
}
|
||||
|
||||
for (next = current ; (datalen - (next - data) > 0) ; ++next)
|
||||
if (*next == '\r')
|
||||
for (next = current ; (datalen - (next - data) > 0) ; ++next) {
|
||||
if (*next == '\r') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((datalen - (next - data) >= 2) && !strncmp(next, "\r\n", 2)) {
|
||||
if ((next - current) > 0) {
|
||||
@@ -1187,7 +1190,7 @@ raop_handler_teardown(raop_conn_t *conn,
|
||||
if (val == 96) {
|
||||
teardown_96 = true;
|
||||
} else if (val == 110) {
|
||||
teardown_110 = true;
|
||||
teardown_110 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ raop_ntp_thread(void *arg)
|
||||
(double) t2 / SECOND_IN_NSECS, str);
|
||||
free(str);
|
||||
}
|
||||
// The iOS client device sends its time in seconds relative to an arbitrary Epoch (the last boot).
|
||||
// The iOS client device sends its time in seconds relative to an arbitrary Epoch (the last boot).
|
||||
// For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970.
|
||||
// This means we have to expect some rather huge offset, but its growth or shrink over time should be small.
|
||||
|
||||
|
||||
@@ -195,7 +195,6 @@ raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, co
|
||||
return raop_rtp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
raop_rtp_destroy(raop_rtp_t *raop_rtp)
|
||||
{
|
||||
@@ -515,7 +514,7 @@ raop_rtp_thread_udp(void *arg)
|
||||
} else {
|
||||
client_ntp_sync_prev = raop_rtp->client_ntp_sync;
|
||||
rtp_sync_prev = raop_rtp->rtp_sync;
|
||||
}
|
||||
}
|
||||
raop_rtp->rtp_sync = byteutils_get_int_be(packet, 4);
|
||||
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
|
||||
raop_rtp->client_ntp_sync = raop_remote_timestamp_to_nano_seconds(raop_rtp->ntp, sync_ntp_raw);
|
||||
@@ -564,20 +563,20 @@ raop_rtp_thread_udp(void *arg)
|
||||
* three times; the secnum and rtp_timestamp increment according to the same pattern as
|
||||
* AAC-ELD packets with audio content.*/
|
||||
|
||||
/* When the ALAC audio stream starts, the initial packets are length-44 packets with
|
||||
* the same 32-byte encrypted payload which after decryption is the beginning of a
|
||||
* 32-byte ALAC packet, presumably with format information, but not actual audio data.
|
||||
* The secnum and rtp_timestamp in the packet header increment according to the same
|
||||
* pattern as ALAC packets with audio content */
|
||||
/* When the ALAC audio stream starts, the initial packets are length-44 packets with
|
||||
* the same 32-byte encrypted payload which after decryption is the beginning of a
|
||||
* 32-byte ALAC packet, presumably with format information, but not actual audio data.
|
||||
* The secnum and rtp_timestamp in the packet header increment according to the same
|
||||
* pattern as ALAC packets with audio content */
|
||||
|
||||
/* The first ALAC packet with data seems to be decoded just before the first sync event
|
||||
* so its dequeuing should be delayed until the first rtp sync has occurred */
|
||||
/* The first ALAC packet with data seems to be decoded just before the first sync event
|
||||
* so its dequeuing should be delayed until the first rtp sync has occurred */
|
||||
|
||||
|
||||
if (FD_ISSET(raop_rtp->dsock, &rfds)) {
|
||||
if (!raop_rtp->initial_sync && !video_arrival_offset) {
|
||||
if (FD_ISSET(raop_rtp->dsock, &rfds)) {
|
||||
if (!raop_rtp->initial_sync && !video_arrival_offset) {
|
||||
video_arrival_offset = raop_ntp_get_video_arrival_offset(raop_rtp->ntp);
|
||||
}
|
||||
}
|
||||
//logger_log(raop_rtp->logger, LOGGER_INFO, "Would have data packet in queue");
|
||||
// Receiving audio data here
|
||||
saddrlen = sizeof(saddr);
|
||||
@@ -594,17 +593,17 @@ raop_rtp_thread_udp(void *arg)
|
||||
free (str);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!raop_rtp->initial_sync && raop_rtp->ct == 8 && video_arrival_offset) {
|
||||
if (!raop_rtp->initial_sync && raop_rtp->ct == 8 && video_arrival_offset) {
|
||||
/* estimate a fake initial remote timestamp for video synchronization with AAC audio before the first rtp sync */
|
||||
uint64_t ts = raop_ntp_get_local_time() - video_arrival_offset;
|
||||
double delay = DELAY_AAC;
|
||||
ts += (uint64_t) (delay * SEC);
|
||||
raop_rtp->client_ntp_sync = ts;
|
||||
raop_rtp->rtp_sync = byteutils_get_int_be(packet, 4);
|
||||
raop_rtp->initial_sync = true;
|
||||
}
|
||||
uint64_t ts = raop_ntp_get_local_time() - video_arrival_offset;
|
||||
double delay = DELAY_AAC;
|
||||
ts += (uint64_t) (delay * SEC);
|
||||
raop_rtp->client_ntp_sync = ts;
|
||||
raop_rtp->rtp_sync = byteutils_get_int_be(packet, 4);
|
||||
raop_rtp->initial_sync = true;
|
||||
}
|
||||
|
||||
if (packetlen == 16 && memcmp(packet + 12, no_data_marker, 4) == 0) {
|
||||
/* this is a "no data" packet */
|
||||
@@ -612,12 +611,12 @@ raop_rtp_thread_udp(void *arg)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (raop_rtp->ct == 2 && packetlen == 44) continue; /* ignore the ALAC packets with format information only. */
|
||||
if (raop_rtp->ct == 2 && packetlen == 44) continue; /* ignore the ALAC packets with format information only. */
|
||||
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, 1);
|
||||
assert(result >= 0);
|
||||
|
||||
if (!raop_rtp->initial_sync) {
|
||||
if (!raop_rtp->initial_sync) {
|
||||
/* wait until the first sync before dequeing ALAC */
|
||||
continue;
|
||||
} else {
|
||||
|
||||
@@ -330,13 +330,13 @@ raop_rtp_mirror_thread(void *arg)
|
||||
/*packet[0:3] contains the payload size */
|
||||
int payload_size = byteutils_get_int(packet, 0);
|
||||
char packet_description[13] = {0};
|
||||
char *p = packet_description;
|
||||
char *p = packet_description;
|
||||
int n = sizeof(packet_description);
|
||||
for (int i = 4; i < 8; i++) {
|
||||
for (int i = 4; i < 8; i++) {
|
||||
snprintf(p, n, "%2.2x ", (unsigned int) packet[i]);
|
||||
n -= 3;
|
||||
p += 3;
|
||||
}
|
||||
}
|
||||
ntp_timestamp_raw = byteutils_get_long(packet, 8);
|
||||
ntp_timestamp_remote = raop_ntp_timestamp_to_nano_seconds(ntp_timestamp_raw, false);
|
||||
if (first_packet) {
|
||||
@@ -345,14 +345,14 @@ raop_rtp_mirror_thread(void *arg)
|
||||
first_packet = false;
|
||||
}
|
||||
|
||||
/* packet[4] + packet[5] identify the payload type: values seen are: *
|
||||
/* packet[4] + packet[5] identify the payload type: values seen are: *
|
||||
* 0x00 0x00: encrypted packet containing a non-IDR type 1 VCL NAL unit *
|
||||
* 0x00 0x10: encrypted packet containing an IDR type 5 VCL NAL unit *
|
||||
* 0x01 0x00: unencrypted packet containing a type 7 SPS NAL + a type 8 PPS NAL unit *
|
||||
* 0x02 0x00: unencrypted packet (old protocol) no payload, sent once every second *
|
||||
* 0x05 0x00 unencrypted packet with a "streaming report", sent once per second. */
|
||||
|
||||
/* packet[6] + packet[7] may list a payload "option": values seen are: *
|
||||
/* packet[6] + packet[7] may list a payload "option": values seen are: *
|
||||
* 0x00 0x00 : encrypted and "streaming report" packets *
|
||||
* 0x1e 0x00 : old protocol (seen in AirMyPC) no-payload once-per-second packets *
|
||||
* 0x16 0x01 : seen in most unencrypted h264 SPS+PPS packets *
|
||||
@@ -395,7 +395,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
break;
|
||||
}
|
||||
|
||||
switch (packet[4]) {
|
||||
switch (packet[4]) {
|
||||
case 0x00:
|
||||
// Normal video data (VCL NAL)
|
||||
|
||||
@@ -415,7 +415,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
}
|
||||
|
||||
unsigned char* payload_out;
|
||||
unsigned char* payload_decrypted;
|
||||
unsigned char* payload_decrypted;
|
||||
/*
|
||||
* nal_types:1 Coded non-partitioned slice of a non-IDR picture
|
||||
* 5 Coded non-partitioned slice of an IDR picture
|
||||
@@ -437,7 +437,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
"raop_rtp_mirror: prepended sps_pps timestamp does not match timestamp of "
|
||||
"video payload\n%llu\n%llu , discarding", ntp_timestamp_raw, ntp_timestamp_nal);
|
||||
free (sps_pps);
|
||||
sps_pps = NULL;
|
||||
sps_pps = NULL;
|
||||
prepend_sps_pps = false;
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
payload_decrypted = payload_out + sps_pps_len;
|
||||
memcpy(payload_out, sps_pps, sps_pps_len);
|
||||
free (sps_pps);
|
||||
sps_pps = NULL;
|
||||
sps_pps = NULL;
|
||||
} else {
|
||||
payload_out = (unsigned char*) malloc(payload_size);
|
||||
payload_decrypted = payload_out;
|
||||
@@ -474,11 +474,11 @@ raop_rtp_mirror_thread(void *arg)
|
||||
valid_data = false;
|
||||
break;
|
||||
}
|
||||
int nalu_type;
|
||||
if (h265_video) {
|
||||
int nalu_type;
|
||||
if (h265_video) {
|
||||
nalu_type = payload_decrypted[nalu_size] & 0x7e >> 1;;
|
||||
//logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG," h265 video, NALU type %d, size %d", nalu_type, nc_len);
|
||||
} else {
|
||||
} else {
|
||||
nalu_type = payload_decrypted[nalu_size] & 0x1f;
|
||||
int ref_idc = (payload_decrypted[nalu_size] >> 5);
|
||||
switch (nalu_type) {
|
||||
@@ -486,7 +486,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
case 5: /*IDR, slice_layer_without_partitioning */
|
||||
case 1: /*non-IDR, slice_layer_without_partitioning */
|
||||
break;
|
||||
case 2: /* slice data partition A */
|
||||
case 2: /* slice data partition A */
|
||||
case 3: /* slice data partition B */
|
||||
case 4: /* slice data partition C */
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_INFO,
|
||||
@@ -526,9 +526,9 @@ raop_rtp_mirror_thread(void *arg)
|
||||
"unexpected non-VCL NAL unit: nalu_type = %d, ref_idc = %d, nalu_size = %d,"
|
||||
"processed bytes %d, payloadsize = %d nalus_count = %d",
|
||||
nalu_type, ref_idc, nc_len, nalu_size, payload_size, nalus_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
nalu_size += nc_len;
|
||||
}
|
||||
if (nalu_size != payload_size) valid_data = false;
|
||||
@@ -540,7 +540,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
|
||||
payload_decrypted = NULL;
|
||||
video_decode_struct video_data;
|
||||
video_data.is_h265 = h265_video;
|
||||
video_data.is_h265 = h265_video;
|
||||
video_data.ntp_time_local = ntp_timestamp_local;
|
||||
video_data.ntp_time_remote = ntp_timestamp_remote;
|
||||
video_data.nal_count = nalus_count; /*nal_count will be the number of nal units in the packet */
|
||||
@@ -571,13 +571,13 @@ raop_rtp_mirror_thread(void *arg)
|
||||
bytes 56-59 width
|
||||
bytes 60-63 height
|
||||
bytes 64-127 all 0x0
|
||||
*/
|
||||
*/
|
||||
|
||||
// The information in the payload contains an SPS and a PPS NAL
|
||||
// The sps_pps is not encrypted
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived unencrypted codec packet from client:"
|
||||
" payload_size %d header %s ts_client = %8.6f",
|
||||
payload_size, packet_description, (double) ntp_timestamp_remote / SEC);
|
||||
payload_size, packet_description, (double) ntp_timestamp_remote / SEC);
|
||||
|
||||
if (packet[6] == 0x56 || packet[6] == 0x5e) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "This packet indicates video stream is stopping");
|
||||
@@ -614,7 +614,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror width_source = %f height_source = %f width = %f height = %f",
|
||||
width_source, height_source, width, height);
|
||||
|
||||
if (payload_size == 0) {
|
||||
if (payload_size == 0) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror: received type 0x01 packet with no payload:\n"
|
||||
"this indicates non-h264 video but Airplay features bit 42 (SupportsScreenMultiCodec) is not set\n"
|
||||
"use startup option \"-h265\" to set this bit and support h265 (4K) video");
|
||||
@@ -625,7 +625,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
free(sps_pps);
|
||||
sps_pps = NULL;
|
||||
}
|
||||
/* test for a H265 VPS/SPS/PPS */
|
||||
/* test for a H265 VPS/SPS/PPS */
|
||||
unsigned char hvc1[] = { 0x68, 0x76, 0x63, 0x31 };
|
||||
|
||||
if (!memcmp(payload + 4, hvc1, 4)) {
|
||||
@@ -677,7 +677,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
break;
|
||||
}
|
||||
sps_size = byteutils_get_short_be(ptr, 3);
|
||||
ptr += 5;
|
||||
ptr += 5;
|
||||
sps = ptr;
|
||||
if (logger_debug) {
|
||||
char *str = utils_data_to_string(sps, sps_size, 16);
|
||||
@@ -690,12 +690,12 @@ raop_rtp_mirror_thread(void *arg)
|
||||
raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls);
|
||||
break;
|
||||
}
|
||||
pps_size = byteutils_get_short_be(ptr, 3);
|
||||
pps_size = byteutils_get_short_be(ptr, 3);
|
||||
ptr += 5;
|
||||
pps = ptr;
|
||||
if (logger_debug) {
|
||||
char *str = utils_data_to_string(pps, pps_size, 16);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "h265 pps size %d\n%s",pps_size, str);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "h265 pps size %d\n%s",pps_size, str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
@@ -802,7 +802,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
|
||||
int plist_size = payload_size;
|
||||
if (payload_size > 25000) {
|
||||
plist_size = payload_size - 25000;
|
||||
plist_size = payload_size - 25000;
|
||||
if (logger_debug) {
|
||||
char *str = utils_data_to_string(payload + plist_size, 16, 16);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
|
||||
@@ -848,7 +848,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror exiting TCP thread");
|
||||
if (conn_reset&& raop_rtp_mirror->callbacks.conn_reset) {
|
||||
raop_rtp_mirror->callbacks.conn_reset(raop_rtp_mirror->callbacks.cls, 1);
|
||||
raop_rtp_mirror->callbacks.conn_reset(raop_rtp_mirror->callbacks.cls, 1);
|
||||
}
|
||||
|
||||
if (unsupported_codec) {
|
||||
|
||||
Reference in New Issue
Block a user