should have been part of previous commit

This commit is contained in:
fduncanh
2021-08-10 01:19:39 -04:00
parent 6014548306
commit 743f92d114
3 changed files with 442 additions and 72 deletions

View File

@@ -363,10 +363,16 @@ void raop_set_display(raop_t *raop, unsigned short width, unsigned short height,
if (width) raop->display_width = width;
if (height) raop->display_height = height;
if (refresh_rate) raop->display_refresh_rate = refresh_rate;
// these must fit into a single byte
if (refresh_rate > 255) refresh_rate = 255;
if (max_fps > 255) max_fps = 255;
if (refresh_rate) raop->display_refresh_rate = refresh_rate;
if (max_fps) raop->display_max_fps = max_fps;
if (raop->display_max_fps > raop->display_refresh_rate) {
raop->display_max_fps = raop->display_refresh_rate;
raop->display_max_fps = raop->display_refresh_rate;
}
}

View File

@@ -1,73 +1,437 @@
#ifndef RAOP_H
#define RAOP_H
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include "dnssd.h"
#include "stream.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "raop.h"
#include "raop_rtp.h"
#include "raop_rtp.h"
#include "pairing.h"
#include "httpd.h"
#include "global.h"
#include "fairplay.h"
#include "netutils.h"
#include "logger.h"
#include "compat.h"
#include "raop_rtp_mirror.h"
#include "raop_ntp.h"
#if defined (WIN32) && defined(DLL_EXPORT)
# define RAOP_API __declspec(dllexport)
#else
# define RAOP_API
#endif
struct raop_s {
/* Callbacks for audio and video */
raop_callbacks_t callbacks;
#ifdef __cplusplus
extern "C" {
#endif
/* Logger instance */
logger_t *logger;
/* Pairing, HTTP daemon and RSA key */
pairing_t *pairing;
httpd_t *httpd;
/* Define syslog style log levels */
#define RAOP_LOG_EMERG 0 /* system is unusable */
#define RAOP_LOG_ALERT 1 /* action must be taken immediately */
#define RAOP_LOG_CRIT 2 /* critical conditions */
#define RAOP_LOG_ERR 3 /* error conditions */
#define RAOP_LOG_WARNING 4 /* warning conditions */
#define RAOP_LOG_NOTICE 5 /* normal but significant condition */
#define RAOP_LOG_INFO 6 /* informational */
#define RAOP_LOG_DEBUG 7 /* debug-level messages */
dnssd_t *dnssd;
unsigned short port;
unsigned short timing_lport;
unsigned short control_lport;
unsigned short data_lport;
unsigned short mirror_data_lport;
typedef struct raop_s raop_t;
typedef void (*raop_log_callback_t)(void *cls, int level, const char *msg);
struct raop_callbacks_s {
void* cls;
void (*audio_process)(void *cls, raop_ntp_t *ntp, aac_decode_struct *data);
void (*video_process)(void *cls, raop_ntp_t *ntp, h264_decode_struct *data);
/* Optional but recommended callback functions */
void (*conn_init)(void *cls);
void (*conn_destroy)(void *cls);
void (*audio_flush)(void *cls);
void (*video_flush)(void *cls);
void (*audio_set_volume)(void *cls, float volume);
void (*audio_set_metadata)(void *cls, const void *buffer, int buflen);
void (*audio_set_coverart)(void *cls, const void *buffer, int buflen);
void (*audio_remote_control_id)(void *cls, const char *dacp_id, const char *active_remote_header);
void (*audio_set_progress)(void *cls, unsigned int start, unsigned int curr, unsigned int end);
unsigned short display_width;
unsigned short display_height;
unsigned short display_refresh_rate;
unsigned short display_max_fps;
};
typedef struct raop_callbacks_s raop_callbacks_t;
RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks);
struct raop_conn_s {
raop_t *raop;
raop_ntp_t *raop_ntp;
raop_rtp_t *raop_rtp;
raop_rtp_mirror_t *raop_rtp_mirror;
fairplay_t *fairplay;
pairing_session_t *pairing;
RAOP_API void raop_set_log_level(raop_t *raop, int level);
RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls);
RAOP_API void raop_set_display(raop_t *raop, unsigned short width, unsigned short height,
unsigned short refresh_rate, unsigned short max_fps);
RAOP_API void raop_set_port(raop_t *raop, unsigned short port);
RAOP_API void raop_set_udp_ports(raop_t *raop, unsigned short port[3]);
RAOP_API void raop_set_tcp_ports(raop_t *raop, unsigned short port[2]);
RAOP_API unsigned short raop_get_port(raop_t *raop);
RAOP_API void *raop_get_callback_cls(raop_t *raop);
RAOP_API int raop_start(raop_t *raop, unsigned short *port);
RAOP_API int raop_is_running(raop_t *raop);
RAOP_API void raop_stop(raop_t *raop);
RAOP_API void raop_set_dnssd(raop_t *raop, dnssd_t *dnssd);
RAOP_API void raop_destroy(raop_t *raop);
unsigned char *local;
int locallen;
#ifdef __cplusplus
unsigned char *remote;
int remotelen;
};
typedef struct raop_conn_s raop_conn_t;
#include "raop_handlers.h"
static void *
conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen) {
raop_t *raop = opaque;
raop_conn_t *conn;
assert(raop);
conn = calloc(1, sizeof(raop_conn_t));
if (!conn) {
return NULL;
}
conn->raop = raop;
conn->raop_rtp = NULL;
conn->raop_ntp = NULL;
conn->fairplay = fairplay_init(raop->logger);
if (!conn->fairplay) {
free(conn);
return NULL;
}
conn->pairing = pairing_session_init(raop->pairing);
if (!conn->pairing) {
fairplay_destroy(conn->fairplay);
free(conn);
return NULL;
}
if (locallen == 4) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Local: %d.%d.%d.%d",
local[0], local[1], local[2], local[3]);
} else if (locallen == 16) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Local: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
local[0], local[1], local[2], local[3], local[4], local[5], local[6], local[7],
local[8], local[9], local[10], local[11], local[12], local[13], local[14], local[15]);
}
if (remotelen == 4) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Remote: %d.%d.%d.%d",
remote[0], remote[1], remote[2], remote[3]);
} else if (remotelen == 16) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Remote: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
remote[0], remote[1], remote[2], remote[3], remote[4], remote[5], remote[6], remote[7],
remote[8], remote[9], remote[10], remote[11], remote[12], remote[13], remote[14], remote[15]);
}
conn->local = malloc(locallen);
assert(conn->local);
memcpy(conn->local, local, locallen);
conn->remote = malloc(remotelen);
assert(conn->remote);
memcpy(conn->remote, remote, remotelen);
conn->locallen = locallen;
conn->remotelen = remotelen;
if (raop->callbacks.conn_init) {
raop->callbacks.conn_init(raop->callbacks.cls);
}
return conn;
}
static void
conn_request(void *ptr, http_request_t *request, http_response_t **response) {
raop_conn_t *conn = ptr;
logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
const char *method;
const char *url;
const char *cseq;
char *response_data = NULL;
int response_datalen = 0;
method = http_request_get_method(request);
url = http_request_get_url(request);
cseq = http_request_get_header(request, "CSeq");
if (!method || !cseq) {
return;
}
*response = http_response_init("RTSP/1.0", 200, "OK");
http_response_add_header(*response, "CSeq", cseq);
//http_response_add_header(*response, "Apple-Jack-Status", "connected; type=analog");
http_response_add_header(*response, "Server", "AirTunes/220.68");
logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
raop_handler_t handler = NULL;
if (!strcmp(method, "GET") && !strcmp(url, "/info")) {
handler = &raop_handler_info;
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-setup")) {
handler = &raop_handler_pairsetup;
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-verify")) {
handler = &raop_handler_pairverify;
} else if (!strcmp(method, "POST") && !strcmp(url, "/fp-setup")) {
handler = &raop_handler_fpsetup;
} else if (!strcmp(method, "OPTIONS")) {
handler = &raop_handler_options;
} else if (!strcmp(method, "SETUP")) {
handler = &raop_handler_setup;
} else if (!strcmp(method, "GET_PARAMETER")) {
handler = &raop_handler_get_parameter;
} else if (!strcmp(method, "SET_PARAMETER")) {
handler = &raop_handler_set_parameter;
} else if (!strcmp(method, "POST") && !strcmp(url, "/feedback")) {
handler = &raop_handler_feedback;
} else if (!strcmp(method, "RECORD")) {
handler = &raop_handler_record;
} else if (!strcmp(method, "FLUSH")) {
const char *rtpinfo;
int next_seq = -1;
rtpinfo = http_request_get_header(request, "RTP-Info");
if (rtpinfo) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo);
if (!strncmp(rtpinfo, "seq=", 4)) {
next_seq = strtol(rtpinfo + 4, NULL, 10);
}
}
if (conn->raop_rtp) {
raop_rtp_flush(conn->raop_rtp, next_seq);
} else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH");
}
} else if (!strcmp(method, "TEARDOWN")) {
//http_response_add_header(*response, "Connection", "close");
if (conn->raop_rtp != NULL && raop_rtp_is_running(conn->raop_rtp)) {
/* Destroy our RTP session */
raop_rtp_stop(conn->raop_rtp);
} else if (conn->raop_rtp_mirror) {
/* Destroy our sessions */
raop_rtp_destroy(conn->raop_rtp);
conn->raop_rtp = NULL;
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
conn->raop_rtp_mirror = NULL;
}
}
if (handler != NULL) {
handler(conn, request, *response, &response_data, &response_datalen);
}
http_response_finish(*response, response_data, response_datalen);
if (response_data) {
free(response_data);
response_data = NULL;
response_datalen = 0;
}
}
static void
conn_destroy(void *ptr) {
raop_conn_t *conn = ptr;
logger_log(conn->raop->logger, LOGGER_INFO, "Destroying connection");
if (conn->raop->callbacks.conn_destroy) {
conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls);
}
if (conn->raop_ntp) {
raop_ntp_destroy(conn->raop_ntp);
}
if (conn->raop_rtp) {
/* This is done in case TEARDOWN was not called */
raop_rtp_destroy(conn->raop_rtp);
}
if (conn->raop_rtp_mirror) {
/* This is done in case TEARDOWN was not called */
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
}
conn->raop->callbacks.video_flush(conn->raop->callbacks.cls);
free(conn->local);
free(conn->remote);
pairing_session_destroy(conn->pairing);
fairplay_destroy(conn->fairplay);
free(conn);
}
raop_t *
raop_init(int max_clients, raop_callbacks_t *callbacks) {
raop_t *raop;
pairing_t *pairing;
httpd_t *httpd;
httpd_callbacks_t httpd_cbs;
assert(callbacks);
assert(max_clients > 0);
assert(max_clients < 100);
/* Initialize the network */
if (netutils_init() < 0) {
return NULL;
}
/* Validate the callbacks structure */
if (!callbacks->audio_process ||
!callbacks->video_process) {
return NULL;
}
/* Allocate the raop_t structure */
raop = calloc(1, sizeof(raop_t));
if (!raop) {
return NULL;
}
/* Initialize the logger */
raop->logger = logger_init();
pairing = pairing_init_generate();
if (!pairing) {
free(raop);
return NULL;
}
/* Set HTTP callbacks to our handlers */
memset(&httpd_cbs, 0, sizeof(httpd_cbs));
httpd_cbs.opaque = raop;
httpd_cbs.conn_init = &conn_init;
httpd_cbs.conn_request = &conn_request;
httpd_cbs.conn_destroy = &conn_destroy;
/* Initialize the http daemon */
httpd = httpd_init(raop->logger, &httpd_cbs, max_clients);
if (!httpd) {
pairing_destroy(pairing);
free(raop);
return NULL;
}
/* Copy callbacks structure */
memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t));
raop->pairing = pairing;
raop->httpd = httpd;
/* initialize network port list */
raop->port = 0;
raop->timing_lport = 0;
raop->control_lport = 0;
raop->data_lport = 0;
raop->mirror_data_lport = 0;
/* initialize display width, height, refresh_rate, max_fps */
raop->display_width = 1920;
raop->display_height = 1080;
raop->display_refresh_rate = 60;
raop->display_max_fps = 30;
return raop;
}
void
raop_destroy(raop_t *raop) {
if (raop) {
raop_stop(raop);
pairing_destroy(raop->pairing);
httpd_destroy(raop->httpd);
logger_destroy(raop->logger);
free(raop);
/* Cleanup the network */
netutils_cleanup();
}
}
int
raop_is_running(raop_t *raop) {
assert(raop);
return httpd_is_running(raop->httpd);
}
void
raop_set_log_level(raop_t *raop, int level) {
assert(raop);
logger_set_level(raop->logger, level);
}
void raop_set_display(raop_t *raop, unsigned short width, unsigned short height,
unsigned short refresh_rate, unsigned short max_fps){
assert(raop);
if (width) raop->display_width = width;
if (height) raop->display_height = height;
// these must fit into a single byte
if (refresh_rate > 255) refresh_rate = 255;
if (max_fps > 255) max_fps = 255;
if (refresh_rate) raop->display_refresh_rate = refresh_rate;
if (max_fps) raop->display_max_fps = max_fps;
if (raop->display_max_fps > raop->display_refresh_rate) {
raop->display_max_fps = raop->display_refresh_rate;
}
}
void
raop_set_port(raop_t *raop, unsigned short port) {
assert(raop);
raop->port = port;
}
void
raop_set_udp_ports(raop_t *raop, unsigned short udp[3]) {
assert(raop);
raop->timing_lport = udp[0];
raop->control_lport = udp[1];
raop->data_lport = udp[2];
}
void
raop_set_tcp_ports(raop_t *raop, unsigned short tcp[2]) {
assert(raop);
raop->mirror_data_lport = tcp[0];
raop->port = tcp[1];
}
unsigned short
raop_get_port(raop_t *raop) {
assert(raop);
return raop->port;
}
void *
raop_get_callback_cls(raop_t *raop) {
assert(raop);
return raop->callbacks.cls;
}
void
raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls) {
assert(raop);
logger_set_callback(raop->logger, callback, cls);
}
void
raop_set_dnssd(raop_t *raop, dnssd_t *dnssd) {
assert(dnssd);
raop->dnssd = dnssd;
}
int
raop_start(raop_t *raop, unsigned short *port) {
assert(raop);
assert(port);
return httpd_start(raop->httpd, port);
}
void
raop_stop(raop_t *raop) {
assert(raop);
httpd_stop(raop->httpd);
}
#endif
#endif

View File

@@ -102,20 +102,20 @@ raop_handler_info(raop_conn_t *conn,
plist_t audio_latencies_node = plist_new_array();
plist_t audio_latencies_0_node = plist_new_dict();
plist_t audio_latencies_0_output_latency_micros_node = plist_new_bool(0);
plist_t audio_latencies_0_output_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_0_type_node = plist_new_uint(100);
plist_t audio_latencies_0_audio_type_node = plist_new_string("default");
plist_t audio_latencies_0_input_latency_micros_node = plist_new_bool(0);
plist_t audio_latencies_0_input_latency_micros_node = plist_new_uint(0);
plist_dict_set_item(audio_latencies_0_node, "outputLatencyMicros", audio_latencies_0_output_latency_micros_node);
plist_dict_set_item(audio_latencies_0_node, "type", audio_latencies_0_type_node);
plist_dict_set_item(audio_latencies_0_node, "audioType", audio_latencies_0_audio_type_node);
plist_dict_set_item(audio_latencies_0_node, "inputLatencyMicros", audio_latencies_0_input_latency_micros_node);
plist_array_append_item(audio_latencies_node, audio_latencies_0_node);
plist_t audio_latencies_1_node = plist_new_dict();
plist_t audio_latencies_1_output_latency_micros_node = plist_new_bool(0);
plist_t audio_latencies_1_output_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_1_type_node = plist_new_uint(101);
plist_t audio_latencies_1_audio_type_node = plist_new_string("default");
plist_t audio_latencies_1_input_latency_micros_node = plist_new_bool(0);
plist_t audio_latencies_1_input_latency_micros_node = plist_new_uint(0);
plist_dict_set_item(audio_latencies_1_node, "outputLatencyMicros", audio_latencies_1_output_latency_micros_node);
plist_dict_set_item(audio_latencies_1_node, "type", audio_latencies_1_type_node);
plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node);
@@ -134,13 +134,13 @@ raop_handler_info(raop_conn_t *conn,
plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925");
plist_t displays_0_width_physical_node = plist_new_uint(0);
plist_t displays_0_height_physical_node = plist_new_uint(0);
plist_t displays_0_width_node = plist_new_uint(conn->raop->display_width);
plist_t displays_0_height_node = plist_new_uint(conn->raop->display_height);
plist_t displays_0_width_pixels_node = plist_new_uint(conn->raop->display_width);
plist_t displays_0_height_pixels_node = plist_new_uint(conn->raop->display_height);
plist_t displays_0_width_node = plist_new_uint((uint16_t) conn->raop->display_width);
plist_t displays_0_height_node = plist_new_uint((uint16_t) conn->raop->display_height);
plist_t displays_0_width_pixels_node = plist_new_uint((uint16_t) conn->raop->display_width);
plist_t displays_0_height_pixels_node = plist_new_uint((uint16_t) conn->raop->display_height);
plist_t displays_0_rotation_node = plist_new_bool(0);
plist_t displays_0_refresh_rate_node = plist_new_uint(conn->raop->display_refresh_rate);
plist_t displays_0_max_fps_node = plist_new_uint(conn->raop->display_max_fps);
plist_t displays_0_refresh_rate_node = plist_new_uint((uint8_t) conn->raop->display_refresh_rate);
plist_t displays_0_max_fps_node = plist_new_uint((uint8_t) conn->raop->display_max_fps);
plist_t displays_0_overscanned_node = plist_new_bool(1);
plist_t displays_0_features = plist_new_uint(14);