2020-04-15 16:24:10 +03:00
/*
* Copyright ( c ) 2019 dsafa22 , All Rights Reserved .
*
* 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 .
2023-02-09 10:32:36 -05:00
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* modified by fduncanh 2021 - 2023
2020-04-15 16:24:10 +03:00
*/
# include "raop_rtp_mirror.h"
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <assert.h>
# include <errno.h>
# include <stdbool.h>
2022-09-01 11:41:17 -04:00
# ifdef _WIN32
# include <winsock2.h>
# else
2021-07-31 19:29:58 -04:00
# include <netinet/tcp.h>
2022-09-01 11:41:17 -04:00
# endif
2020-04-15 16:24:10 +03:00
# include "raop.h"
# include "netutils.h"
# include "compat.h"
# include "logger.h"
# include "byteutils.h"
# include "mirror_buffer.h"
# include "stream.h"
2022-02-05 00:55:13 -05:00
# include "utils.h"
# include "plist/plist.h"
2020-04-15 16:24:10 +03:00
2022-09-01 11:41:17 -04:00
# ifdef _WIN32
# define CAST (char *)
2022-09-02 07:46:22 -04:00
/* are these keepalive settings for WIN32 correct? */
/* (taken from https://github.com/wegank/ludimus) */
# define TCP_KEEPALIVE 3
# define TCP_KEEPCNT 16
# define TCP_KEEPIDLE TCP_KEEPALIVE
# define TCP_KEEPINTVL 17
2022-09-01 11:41:17 -04:00
# else
# define CAST
# endif
2023-02-05 01:59:25 -05:00
# define SECOND_IN_NSECS 1000000000UL
# define SEC SECOND_IN_NSECS
2022-09-01 11:41:17 -04:00
2021-09-04 01:13:12 -04:00
/* for MacOS, where SOL_TCP and TCP_KEEPIDLE are not defined */
# if !defined(SOL_TCP) && defined(IPPROTO_TCP)
# define SOL_TCP IPPROTO_TCP
# endif
# if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE)
# define TCP_KEEPIDLE TCP_KEEPALIVE
# endif
2020-04-15 16:24:10 +03:00
2022-04-22 11:19:31 -04:00
//struct h264codec_s {
// unsigned char compatibility;
// short pps_size;
// short sps_size;
// unsigned char level;
// unsigned char number_of_pps;
// unsigned char* picture_parameter_set;
// unsigned char profile_high;
// unsigned char reserved_3_and_sps;
// unsigned char reserved_6_and_nal;
// unsigned char* sequence_parameter_set;
// unsigned char version;
//};
2020-04-15 16:24:10 +03:00
struct raop_rtp_mirror_s {
logger_t * logger ;
raop_callbacks_t callbacks ;
raop_ntp_t * ntp ;
/* Buffer to handle all resends */
mirror_buffer_t * buffer ;
/* Remote address as sockaddr */
struct sockaddr_storage remote_saddr ;
socklen_t remote_saddr_len ;
/* MUTEX LOCKED VARIABLES START */
/* These variables only edited mutex locked */
int running ;
int joined ;
int flush ;
thread_handle_t thread_mirror ;
mutex_handle_t run_mutex ;
/* MUTEX LOCKED VARIABLES END */
int mirror_data_sock ;
unsigned short mirror_data_lport ;
2021-12-07 04:08:23 -05:00
2022-02-05 00:55:13 -05:00
/* switch for displaying client FPS data */
uint8_t show_client_FPS_data ;
2022-04-22 11:19:31 -04:00
/* SPS and PPS */
int sps_pps_len ;
unsigned char * sps_pps ;
bool sps_pps_waiting ;
2020-04-15 16:24:10 +03:00
2022-04-28 23:10:28 -04:00
} ;
2022-04-27 21:28:29 -04:00
2020-04-15 16:24:10 +03:00
static int
raop_rtp_parse_remote ( raop_rtp_mirror_t * raop_rtp_mirror , const unsigned char * remote , int remotelen )
{
char current [ 25 ] ;
int family ;
int ret ;
assert ( raop_rtp_mirror ) ;
if ( remotelen = = 4 ) {
family = AF_INET ;
} else if ( remotelen = = 16 ) {
family = AF_INET6 ;
} else {
return - 1 ;
}
memset ( current , 0 , sizeof ( current ) ) ;
2023-04-15 04:43:00 -04:00
snprintf ( current , sizeof ( current ) , " %d.%d.%d.%d " , remote [ 0 ] , remote [ 1 ] , remote [ 2 ] , remote [ 3 ] ) ;
2020-04-15 16:24:10 +03:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror parse remote ip = %s " , current ) ;
ret = netutils_parse_address ( family , current ,
& raop_rtp_mirror - > remote_saddr ,
sizeof ( raop_rtp_mirror - > remote_saddr ) ) ;
if ( ret < 0 ) {
return - 1 ;
}
raop_rtp_mirror - > remote_saddr_len = ret ;
return 0 ;
}
# define NO_FLUSH (-42)
raop_rtp_mirror_t * raop_rtp_mirror_init ( logger_t * logger , raop_callbacks_t * callbacks , raop_ntp_t * ntp ,
2021-12-13 16:49:08 -05:00
const unsigned char * remote , int remotelen , const unsigned char * aeskey )
2020-04-15 16:24:10 +03:00
{
raop_rtp_mirror_t * raop_rtp_mirror ;
assert ( logger ) ;
assert ( callbacks ) ;
raop_rtp_mirror = calloc ( 1 , sizeof ( raop_rtp_mirror_t ) ) ;
if ( ! raop_rtp_mirror ) {
return NULL ;
}
raop_rtp_mirror - > logger = logger ;
raop_rtp_mirror - > ntp = ntp ;
2022-04-22 11:19:31 -04:00
raop_rtp_mirror - > sps_pps_len = 0 ;
raop_rtp_mirror - > sps_pps = NULL ;
raop_rtp_mirror - > sps_pps_waiting = false ;
2020-04-15 16:24:10 +03:00
memcpy ( & raop_rtp_mirror - > callbacks , callbacks , sizeof ( raop_callbacks_t ) ) ;
2021-12-13 16:49:08 -05:00
raop_rtp_mirror - > buffer = mirror_buffer_init ( logger , aeskey ) ;
2020-04-15 16:24:10 +03:00
if ( ! raop_rtp_mirror - > buffer ) {
free ( raop_rtp_mirror ) ;
return NULL ;
}
if ( raop_rtp_parse_remote ( raop_rtp_mirror , remote , remotelen ) < 0 ) {
free ( raop_rtp_mirror ) ;
return NULL ;
}
raop_rtp_mirror - > running = 0 ;
raop_rtp_mirror - > joined = 1 ;
raop_rtp_mirror - > flush = NO_FLUSH ;
MUTEX_CREATE ( raop_rtp_mirror - > run_mutex ) ;
return raop_rtp_mirror ;
}
void
2022-01-21 16:49:17 -05:00
raop_rtp_init_mirror_aes ( raop_rtp_mirror_t * raop_rtp_mirror , uint64_t * streamConnectionID )
2020-04-15 16:24:10 +03:00
{
mirror_buffer_init_aes ( raop_rtp_mirror - > buffer , streamConnectionID ) ;
}
//#define DUMP_H264
# define RAOP_PACKET_LEN 32768
/**
* Mirror
*/
static THREAD_RETVAL
raop_rtp_mirror_thread ( void * arg )
{
raop_rtp_mirror_t * raop_rtp_mirror = arg ;
assert ( raop_rtp_mirror ) ;
int stream_fd = - 1 ;
unsigned char packet [ 128 ] ;
memset ( packet , 0 , 128 ) ;
unsigned char * payload = NULL ;
unsigned int readstart = 0 ;
2022-02-02 19:01:59 -05:00
bool conn_reset = false ;
2022-04-28 23:10:28 -04:00
uint64_t ntp_timestamp_nal = 0 ;
uint64_t ntp_timestamp_raw = 0 ;
2023-02-09 03:06:36 -05:00
uint64_t ntp_timestamp_remote = 0 ;
2023-02-10 14:05:08 -05:00
uint64_t ntp_timestamp_local = 0 ;
2022-04-21 00:56:56 -04:00
unsigned char nal_start_code [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x01 } ;
2022-04-01 13:09:20 -04:00
2020-04-15 16:24:10 +03:00
# ifdef DUMP_H264
// C decrypted
FILE * file = fopen ( " /home/pi/Airplay.h264 " , " wb " ) ;
// Encrypted source file
FILE * file_source = fopen ( " /home/pi/Airplay.source " , " wb " ) ;
FILE * file_len = fopen ( " /home/pi/Airplay.len " , " wb " ) ;
# endif
while ( 1 ) {
fd_set rfds ;
struct timeval tv ;
int nfds , ret ;
MUTEX_LOCK ( raop_rtp_mirror - > run_mutex ) ;
if ( ! raop_rtp_mirror - > running ) {
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
2022-02-21 13:10:46 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror->running is no longer true " ) ;
2020-04-15 16:24:10 +03:00
break ;
}
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
2022-04-01 13:09:20 -04:00
/* Set timeout valu to 5ms */
2020-04-15 16:24:10 +03:00
tv . tv_sec = 0 ;
tv . tv_usec = 5000 ;
/* Get the correct nfds value and set rfds */
FD_ZERO ( & rfds ) ;
if ( stream_fd = = - 1 ) {
FD_SET ( raop_rtp_mirror - > mirror_data_sock , & rfds ) ;
nfds = raop_rtp_mirror - > mirror_data_sock + 1 ;
} else {
FD_SET ( stream_fd , & rfds ) ;
nfds = stream_fd + 1 ;
}
ret = select ( nfds , & rfds , NULL , NULL , & tv ) ;
if ( ret = = 0 ) {
/* Timeout happened */
continue ;
} else if ( ret = = - 1 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror error in select " ) ;
break ;
}
2022-10-08 15:23:41 -04:00
if ( stream_fd = = - 1 & &
( raop_rtp_mirror & & raop_rtp_mirror - > mirror_data_sock > = 0 ) & &
FD_ISSET ( raop_rtp_mirror - > mirror_data_sock , & rfds ) ) {
2020-04-15 16:24:10 +03:00
struct sockaddr_storage saddr ;
socklen_t saddrlen ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror accepting client " ) ;
saddrlen = sizeof ( saddr ) ;
stream_fd = accept ( raop_rtp_mirror - > mirror_data_sock , ( struct sockaddr * ) & saddr , & saddrlen ) ;
if ( stream_fd = = - 1 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror error in accept %d %s " , errno , strerror ( errno ) ) ;
break ;
}
// We're calling recv for a certain amount of data, so we need a timeout
struct timeval tv ;
tv . tv_sec = 0 ;
tv . tv_usec = 5000 ;
2022-09-01 11:41:17 -04:00
if ( setsockopt ( stream_fd , SOL_SOCKET , SO_RCVTIMEO , CAST & tv , sizeof ( tv ) ) < 0 ) {
2020-04-15 16:24:10 +03:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror could not set stream socket timeout %d %s " , errno , strerror ( errno ) ) ;
break ;
}
2022-09-01 11:41:17 -04:00
2021-07-31 19:29:58 -04:00
int option ;
option = 1 ;
2022-09-01 11:41:17 -04:00
if ( setsockopt ( stream_fd , SOL_SOCKET , SO_KEEPALIVE , CAST & option , sizeof ( option ) ) < 0 ) {
2021-07-31 19:29:58 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " raop_rtp_mirror could not set stream socket keepalive %d %s " , errno , strerror ( errno ) ) ;
}
option = 60 ;
2022-09-01 11:41:17 -04:00
if ( setsockopt ( stream_fd , SOL_TCP , TCP_KEEPIDLE , CAST & option , sizeof ( option ) ) < 0 ) {
2021-07-31 19:29:58 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " raop_rtp_mirror could not set stream socket keepalive time %d %s " , errno , strerror ( errno ) ) ;
}
option = 10 ;
2022-09-01 11:41:17 -04:00
if ( setsockopt ( stream_fd , SOL_TCP , TCP_KEEPINTVL , CAST & option , sizeof ( option ) ) < 0 ) {
2021-07-31 19:29:58 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " raop_rtp_mirror could not set stream socket keepalive interval %d %s " , errno , strerror ( errno ) ) ;
}
option = 6 ;
2022-09-01 11:41:17 -04:00
if ( setsockopt ( stream_fd , SOL_TCP , TCP_KEEPCNT , CAST & option , sizeof ( option ) ) < 0 ) {
2021-07-31 19:29:58 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " raop_rtp_mirror could not set stream socket keepalive probes %d %s " , errno , strerror ( errno ) ) ;
}
2020-04-15 16:24:10 +03:00
readstart = 0 ;
}
if ( stream_fd ! = - 1 & & FD_ISSET ( stream_fd , & rfds ) ) {
// The first 128 bytes are some kind of header for the payload that follows
while ( payload = = NULL & & readstart < 128 ) {
2022-09-01 11:41:17 -04:00
unsigned char * pos = packet + readstart ;
ret = recv ( stream_fd , CAST pos , 128 - readstart , 0 ) ;
2020-04-15 16:24:10 +03:00
if ( ret < = 0 ) break ;
readstart = readstart + ret ;
}
if ( payload = = NULL & & ret = = 0 ) {
2022-04-23 15:32:18 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror tcp socket is closed, got %d bytes of 128 byte header " , readstart ) ;
FD_CLR ( stream_fd , & rfds ) ;
stream_fd = - 1 ;
continue ;
2020-04-15 16:24:10 +03:00
} else if ( payload = = NULL & & ret = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) continue ; // Timeouts can happen even if the connection is fine
2022-02-05 00:55:13 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror error in header recv: %d %s " , errno , strerror ( errno ) ) ;
2022-02-02 19:01:59 -05:00
if ( errno = = ECONNRESET ) conn_reset = true ; ;
break ;
2020-04-15 16:24:10 +03:00
}
2022-04-15 19:53:38 -04:00
/*packet[0:3] contains the payload size */
2020-04-15 16:24:10 +03:00
int payload_size = byteutils_get_int ( packet , 0 ) ;
2023-02-09 03:06:36 -05:00
char packet_description [ 13 ] = { 0 } ;
char * p = packet_description ;
2023-04-15 04:43:00 -04:00
int n = sizeof ( packet_description ) ;
2023-02-09 03:06:36 -05:00
for ( int i = 4 ; i < 8 ; i + + ) {
2023-04-15 04:43:00 -04:00
snprintf ( p , n , " %2.2x " , ( unsigned int ) packet [ i ] ) ;
n - = 3 ;
2023-02-09 03:06:36 -05:00
p + = 3 ;
}
ntp_timestamp_raw = byteutils_get_long ( packet , 8 ) ;
ntp_timestamp_remote = raop_ntp_timestamp_to_nano_seconds ( ntp_timestamp_raw , false ) ;
2022-04-15 19:53:38 -04:00
/* packet[4] appears to have one of three possible values: *
* 0x00 : encrypted packet *
* 0x01 : unencrypted packet with a SPS and a PPS NAL , sent initially , and also when *
* a change in video format ( e . g . , width , height ) subsequently occurs *
* 0x05 : unencrypted packet with a " streaming report " , sent once per second */
/* encrypted packets have packet[5] = 0x00 or 0x10, and packet[6]= packet[7] = 0x00; *
* encrypted packets immediately following an unencrypted SPS / PPS packet appear to *
* be the only ones with packet [ 5 ] = 0x10 , and almost always have packet [ 5 ] = 0x10 , *
* but occasionally have packet [ 5 ] = 0x00 . */
2023-01-12 17:09:55 -05:00
/* unencrypted SPS/PPS packets have packet[4:7] = 0x01 0x00 (0x16 or 0x56) 0x01 *
2022-04-15 19:53:38 -04:00
* they are followed by an encrypted packet with the same timestamp in packet [ 8 : 15 ] */
2022-04-19 23:51:49 -04:00
/* "streaming report" packages have packet[4:7] = 0x05 0x00 0x00 0x00, and have no *
2022-04-15 19:53:38 -04:00
* timestamp in packet [ 8 : 15 ] */
2022-02-05 00:55:13 -05:00
//unsigned short payload_type = byteutils_get_short(packet, 4) & 0xff;
2021-10-13 09:05:37 -04:00
//unsigned short payload_option = byteutils_get_short(packet, 6);
2020-04-15 16:24:10 +03:00
if ( payload = = NULL ) {
payload = malloc ( payload_size ) ;
readstart = 0 ;
}
while ( readstart < payload_size ) {
// Payload data
2022-09-01 11:41:17 -04:00
unsigned char * pos = payload + readstart ;
ret = recv ( stream_fd , CAST pos , payload_size - readstart , 0 ) ;
2020-04-15 16:24:10 +03:00
if ( ret < = 0 ) break ;
readstart = readstart + ret ;
}
if ( ret = = 0 ) {
2022-03-11 12:01:31 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror tcp socket is closed " ) ;
2020-04-15 16:24:10 +03:00
break ;
} else if ( ret = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) continue ; // Timeouts can happen even if the connection is fine
2022-02-05 00:55:13 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror error in recv: %d %s " , errno , strerror ( errno ) ) ;
2022-02-02 19:11:12 -05:00
if ( errno = = ECONNRESET ) conn_reset = true ;
2022-02-02 19:01:59 -05:00
break ;
2020-04-15 16:24:10 +03:00
}
2022-02-05 00:55:13 -05:00
switch ( packet [ 4 ] ) {
case 0x00 :
2020-04-15 16:24:10 +03:00
// Normal video data (VCL NAL)
// Conveniently, the video data is already stamped with the remote wall clock time,
// so no additional clock syncing needed. The only thing odd here is that the video
// ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just
2023-02-05 01:59:25 -05:00
// counting nano seconds since last boot.
2020-04-15 16:24:10 +03:00
2023-02-10 14:05:08 -05:00
ntp_timestamp_local = raop_ntp_convert_remote_time ( raop_rtp_mirror - > ntp , ntp_timestamp_remote ) ;
2020-04-15 16:24:10 +03:00
uint64_t ntp_now = raop_ntp_get_local_time ( raop_rtp_mirror - > ntp ) ;
2023-02-10 14:05:08 -05:00
int64_t latency = ( ( int64_t ) ntp_now ) - ( ( int64_t ) ntp_timestamp_local ) ;
2023-02-09 03:06:36 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp video: now = %8.6f, ntp = %8.6f, latency = %8.6f, ts = %8.6f, %s " ,
2023-02-10 14:05:08 -05:00
( double ) ntp_now / SEC , ( double ) ntp_timestamp_local / SEC , ( double ) latency / SEC , ( double ) ntp_timestamp_remote / SEC , packet_description ) ;
2020-04-15 16:24:10 +03:00
# ifdef DUMP_H264
fwrite ( payload , payload_size , 1 , file_source ) ;
fwrite ( & readstart , sizeof ( readstart ) , 1 , file_len ) ;
# endif
2022-02-05 00:55:13 -05:00
unsigned char * payload_out ;
unsigned char * payload_decrypted ;
2022-04-22 11:19:31 -04:00
if ( ! raop_rtp_mirror - > sps_pps_waiting & & packet [ 5 ] ! = 0x00 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " unexpected: packet[5] = %2.2x, but not preceded by SPS+PPS packet " , packet [ 5 ] ) ;
}
2022-05-05 12:11:29 -04:00
/* if a previous unencrypted packet contains an SPS (type 7) and PPS (type 8) NAL which has not
* yet been sent , it should be prepended to the current NAL . In this case packet [ 5 ] is usually
* 0x10 ; however , the M1 Macs have increased the h264 level , and now the encrypted packet after the
* unencrypted SPS + PPS packet may contain a SEI ( type 6 ) NAL prepended to the next VCL NAL , with
* packet [ 5 ] = 0x00 . Now the flag raop_rtp_mirror - > sps_pps_waiting = true will signal that a
* previous packet contained a SPS NAL + a PPS NAL , that has not yet been sent . This will trigger
* prepending it to the current NAL , and the sps_pps_waiting flag will be set to false after
* it has been prepended . It is not clear if the case packet [ 5 ] = 0x10 will occur when
* raop_rtp_mirror - > sps_pps = false , but if it does , the current code will prepend the stored
2022-05-05 12:15:09 -04:00
* PPS + SPS NAL to the current encrypted NAL , and issue a warning message */
2022-05-05 12:11:29 -04:00
2022-04-22 11:19:31 -04:00
bool prepend_sps_pps = ( raop_rtp_mirror - > sps_pps_waiting | | packet [ 5 ] ! = 0x00 ) ;
2022-02-05 00:55:13 -05:00
if ( prepend_sps_pps ) {
2022-04-22 12:03:33 -04:00
assert ( raop_rtp_mirror - > sps_pps ) ;
2022-04-22 11:19:31 -04:00
payload_out = ( unsigned char * ) malloc ( payload_size + raop_rtp_mirror - > sps_pps_len ) ;
payload_decrypted = payload_out + raop_rtp_mirror - > sps_pps_len ;
memcpy ( payload_out , raop_rtp_mirror - > sps_pps , raop_rtp_mirror - > sps_pps_len ) ;
raop_rtp_mirror - > sps_pps_waiting = false ;
2022-02-05 00:55:13 -05:00
} else {
payload_out = ( unsigned char * ) malloc ( payload_size ) ;
payload_decrypted = payload_out ;
}
2020-04-15 16:24:10 +03:00
// Decrypt data
mirror_buffer_decrypt ( raop_rtp_mirror - > buffer , payload , payload_decrypted , payload_size ) ;
// It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte
// start code for the NAL Byte-Stream Format.
2022-02-05 00:55:13 -05:00
bool valid_data = true ;
2022-02-20 11:23:57 -05:00
int nalu_size = 0 ;
int nalus_count = 0 ;
2022-04-21 02:46:00 -04:00
int nalu_type ; /* 0x01 non-IDR VCL, 0x05 IDR VCL, 0x06 SEI 0x07 SPS, 0x08 PPS */
2020-04-15 16:24:10 +03:00
while ( nalu_size < payload_size ) {
2022-02-20 11:23:57 -05:00
int nc_len = byteutils_get_int_be ( payload_decrypted , nalu_size ) ;
2021-12-08 15:39:50 -05:00
if ( nc_len < 0 | | nalu_size + 4 > payload_size ) {
2022-02-05 00:55:13 -05:00
valid_data = false ;
2021-12-08 15:53:35 -05:00
break ;
2021-12-08 15:39:50 -05:00
}
2022-04-21 00:56:56 -04:00
memcpy ( payload_decrypted + nalu_size , nal_start_code , 4 ) ;
2022-02-05 00:55:13 -05:00
nalu_size + = 4 ;
2021-12-07 05:10:21 -05:00
nalus_count + + ;
2022-04-21 01:40:08 -04:00
if ( payload_decrypted [ nalu_size ] & 0x80 ) valid_data = false ; /* first bit of h264 nalu MUST be 0 ("forbidden_zero_bit") */
nalu_type = payload_decrypted [ nalu_size ] & 0x1f ;
2022-02-05 00:55:13 -05:00
nalu_size + = nc_len ;
2022-05-02 01:44:54 -04:00
if ( nalu_type ! = 1 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " nalu_type = %d, nalu_size = %d, processed bytes %d, payloadsize = %d nalus_count = %d " ,
nalu_type , nc_len , nalu_size , payload_size , nalus_count ) ;
}
}
2022-02-05 00:55:13 -05:00
if ( nalu_size ! = payload_size ) valid_data = false ;
2022-04-01 13:09:20 -04:00
if ( ! valid_data ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " nalu marked as invalid " ) ;
2022-04-16 20:12:30 -04:00
payload_out [ 0 ] = 1 ; /* mark video data as invalid h264 (failed decryption) */
2022-04-01 13:09:20 -04:00
}
2020-04-15 16:24:10 +03:00
# ifdef DUMP_H264
fwrite ( payload_decrypted , payload_size , 1 , file ) ;
# endif
2022-04-16 20:28:13 -04:00
payload_decrypted = NULL ;
h264_decode_struct h264_data ;
2023-02-10 14:05:08 -05:00
h264_data . ntp_time_local = ntp_timestamp_local ;
h264_data . ntp_time_remote = ntp_timestamp_remote ;
2022-04-18 00:47:37 -04:00
h264_data . nal_count = nalus_count ; /*nal_count will be the number of nal units in the packet */
2022-04-16 20:12:30 -04:00
h264_data . data_len = payload_size ;
h264_data . data = payload_out ;
2022-04-01 13:09:20 -04:00
if ( prepend_sps_pps ) {
2022-04-22 11:19:31 -04:00
h264_data . data_len + = raop_rtp_mirror - > sps_pps_len ;
2022-04-16 20:12:30 -04:00
h264_data . nal_count + = 2 ;
2023-02-10 14:05:08 -05:00
if ( ntp_timestamp_raw ! = ntp_timestamp_nal ) {
2022-02-05 00:55:13 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " raop_rtp_mirror: prepended sps_pps timestamp does not match that of video payload " ) ;
}
2022-04-01 13:20:50 -04:00
}
2021-12-08 13:38:12 -05:00
raop_rtp_mirror - > callbacks . video_process ( raop_rtp_mirror - > callbacks . cls , raop_rtp_mirror - > ntp , & h264_data ) ;
2022-02-05 00:55:13 -05:00
free ( payload_out ) ;
break ;
case 0x01 :
2020-04-15 16:24:10 +03:00
// The information in the payload contains an SPS and a PPS NAL
2022-04-01 13:09:20 -04:00
// The sps_pps is not encrypted
2023-02-09 03:06:36 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " \n Received unencryted codec packet from client: payload_size %d header %s ts_client = %8.6f " ,
payload_size , packet_description , ( double ) ntp_timestamp_remote / SEC ) ;
2023-01-12 17:09:55 -05:00
if ( payload_size = = 0 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror, discard type 0x01 packet with no payload " ) ;
break ;
}
2023-02-10 14:05:08 -05:00
ntp_timestamp_nal = ntp_timestamp_raw ;
2022-04-15 19:53:38 -04:00
float width = byteutils_get_float ( packet , 16 ) ;
float height = byteutils_get_float ( packet , 20 ) ;
2020-04-15 16:24:10 +03:00
float width_source = byteutils_get_float ( packet , 40 ) ;
float height_source = byteutils_get_float ( packet , 44 ) ;
2022-04-15 19:53:38 -04:00
if ( width ! = width_source | | height ! = height_source ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror: Unexpected : data %f, %f != width_source = %f, height_source = %f " ,
width , height , width_source , height_source ) ;
}
width = byteutils_get_float ( packet , 48 ) ;
height = byteutils_get_float ( packet , 52 ) ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror: unidentified extra header data %f, %f " , width , height ) ;
width = byteutils_get_float ( packet , 56 ) ;
height = byteutils_get_float ( packet , 60 ) ;
2022-01-10 19:08:48 -05:00
if ( raop_rtp_mirror - > callbacks . video_report_size ) {
raop_rtp_mirror - > callbacks . video_report_size ( raop_rtp_mirror - > callbacks . cls , & width_source , & height_source , & width , & height ) ;
}
2020-04-15 16:24:10 +03:00
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 ) ;
2022-04-01 13:09:20 -04:00
short sps_size = byteutils_get_short_be ( payload , 6 ) ;
unsigned char * sequence_parameter_set = payload + 8 ;
short pps_size = byteutils_get_short_be ( payload , sps_size + 9 ) ;
unsigned char * picture_parameter_set = payload + sps_size + 11 ;
2022-04-15 19:53:38 -04:00
int data_size = 6 ;
char * str = utils_data_to_string ( payload , data_size , 16 ) ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror: sps/pps header size = %d " , data_size ) ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror h264 sps/pps header: \n %s " , str ) ;
free ( str ) ;
str = utils_data_to_string ( sequence_parameter_set , sps_size , 16 ) ;
2022-04-01 13:09:20 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror sps size = %d " , sps_size ) ;
2022-03-24 03:25:48 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror h264 Sequence Parameter Set: \n %s " , str ) ;
free ( str ) ;
2022-04-15 19:53:38 -04:00
str = utils_data_to_string ( picture_parameter_set , pps_size , 16 ) ;
2022-04-01 13:09:20 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror pps size = %d " , pps_size ) ;
2022-03-24 03:25:48 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror h264 Picture Parameter Set: \n %s " , str ) ;
2022-03-24 03:33:37 -04:00
free ( str ) ;
2022-04-15 19:53:38 -04:00
data_size = payload_size - sps_size - pps_size - 11 ;
if ( data_size > 0 ) {
str = utils_data_to_string ( picture_parameter_set + pps_size , data_size , 16 ) ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " remainder size = %d " , data_size ) ;
2022-04-01 13:09:20 -04:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " remainder of sps+pps packet: \n %s " , str ) ;
free ( str ) ;
2022-04-15 19:53:38 -04:00
} else if ( data_size < 0 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " pps_sps error: packet remainder size = %d < 0 " , data_size ) ;
2022-04-01 13:09:20 -04:00
}
2022-03-24 03:33:37 -04:00
2022-04-01 13:09:20 -04:00
// Copy the sps and pps into a buffer to prepend to the next NAL unit.
2022-04-22 11:19:31 -04:00
raop_rtp_mirror - > sps_pps_len = sps_size + pps_size + 8 ;
if ( raop_rtp_mirror - > sps_pps ) {
free ( raop_rtp_mirror - > sps_pps ) ;
2022-04-01 13:09:20 -04:00
}
2022-04-22 11:19:31 -04:00
raop_rtp_mirror - > sps_pps = ( unsigned char * ) malloc ( raop_rtp_mirror - > sps_pps_len ) ;
assert ( raop_rtp_mirror - > sps_pps ) ;
memcpy ( raop_rtp_mirror - > sps_pps , nal_start_code , 4 ) ;
memcpy ( raop_rtp_mirror - > sps_pps + 4 , sequence_parameter_set , sps_size ) ;
memcpy ( raop_rtp_mirror - > sps_pps + sps_size + 4 , nal_start_code , 4 ) ;
memcpy ( raop_rtp_mirror - > sps_pps + sps_size + 8 , payload + sps_size + 11 , pps_size ) ;
raop_rtp_mirror - > sps_pps_waiting = true ;
2020-04-15 16:24:10 +03:00
# ifdef DUMP_H264
2022-04-22 11:19:31 -04:00
fwrite ( raop_rtp_mirror - > sps_pps , raop_rtp_mirror - > sps_pps_len , 1 , file ) ;
2022-04-01 13:20:50 -04:00
# endif
// h264codec_t h264;
2022-04-01 13:09:20 -04:00
// h264.version = payload[0];
// h264.profile_high = payload[1];
// h264.compatibility = payload[2];
// h264.level = payload[3];
// h264.reserved_6_and_nal = payload[4];
// h264.reserved_3_and_sps = payload[5];
// h264.sps_size = sps_size;
// h264.sequence_parameter_set = malloc(h264.sps_size);
// memcpy(h264.sequence_parameter_set, sequence_parameter_set, sps_size);
// h264.number_of_pps = payload[h264.sps_size + 8];
// h264.pps_size = pps_size;
// h264.picture_parameter_set = malloc(h264.pps_size);
// memcpy(h264.picture_parameter_set, picture_parameter_set, pps_size);
2020-04-15 16:24:10 +03:00
2022-02-05 00:55:13 -05:00
break ;
case 0x05 :
2023-02-09 03:06:36 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " \n Received video streaming performance info packet from client: payload_size %d header %s ts_raw = %llu " ,
payload_size , packet_description , ntp_timestamp_raw ) ;
2022-03-24 03:33:37 -04:00
/* payloads with packet[4] = 0x05 have no timestamp, and carry video info from the client as a binary plist *
* Sometimes ( e . g , when the client has a locked screen ) , there is a 25 kB trailer attached to the packet . *
* This 25000 Byte trailer with unidentified content seems to be the same data each time it is sent . */
2022-02-05 00:55:13 -05:00
2022-02-06 12:23:36 -05:00
if ( payload_size & & raop_rtp_mirror - > show_client_FPS_data ) {
2022-02-05 00:55:13 -05:00
//char *str = utils_data_to_string(packet, 128, 16);
//logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "type 5 video packet header:\n%s", str);
//free (str);
int plist_size = payload_size ;
if ( payload_size > 25000 ) {
plist_size = payload_size - 25000 ;
char * str = utils_data_to_string ( payload + plist_size , 16 , 16 ) ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " video_info packet had 25kB trailer; first 16 bytes are: \n %s " , str ) ;
free ( str ) ;
}
if ( plist_size ) {
char * plist_xml ;
uint32_t plist_len ;
plist_t root_node = NULL ;
plist_from_bin ( ( char * ) payload , plist_size , & root_node ) ;
plist_to_xml ( root_node , & plist_xml , & plist_len ) ;
2022-02-06 12:23:36 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_INFO , " %s " , plist_xml ) ;
2022-02-05 00:55:13 -05:00
free ( plist_xml ) ;
}
}
2022-04-15 19:53:38 -04:00
break ;
2022-02-05 00:55:13 -05:00
default :
2023-02-09 03:06:36 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_WARNING , " \n Received unexpected TCP packet from client, size %d, %s ts_raw = raw%llu " ,
payload_size , packet_description , ntp_timestamp_raw ) ;
2022-02-05 00:55:13 -05:00
break ;
2020-04-15 16:24:10 +03:00
}
free ( payload ) ;
payload = NULL ;
memset ( packet , 0 , 128 ) ;
readstart = 0 ;
}
}
/* Close the stream file descriptor */
if ( stream_fd ! = - 1 ) {
closesocket ( stream_fd ) ;
}
# ifdef DUMP_H264
2022-02-05 00:55:13 -05:00
fclose ( file ) ;
2020-04-15 16:24:10 +03:00
fclose ( file_source ) ;
fclose ( file_len ) ;
# endif
// Ensure running reflects the actual state
MUTEX_LOCK ( raop_rtp_mirror - > run_mutex ) ;
raop_rtp_mirror - > running = false ;
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
2022-02-02 19:14:20 -05:00
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror exiting TCP thread " ) ;
2022-02-06 12:23:36 -05:00
if ( conn_reset & & raop_rtp_mirror - > callbacks . conn_reset ) {
2022-02-21 13:31:16 -05:00
const bool video_reset = false ; /* leave "frozen video" showing */
raop_rtp_mirror - > callbacks . conn_reset ( raop_rtp_mirror - > callbacks . cls , 0 , video_reset ) ;
2022-02-02 19:01:59 -05:00
}
2020-04-15 16:24:10 +03:00
return 0 ;
}
2021-12-04 15:22:55 -05:00
static int
2022-04-22 16:01:00 -04:00
raop_rtp_init_mirror_sockets ( raop_rtp_mirror_t * raop_rtp_mirror , int use_ipv6 )
{
assert ( raop_rtp_mirror ) ;
unsigned short dport = raop_rtp_mirror - > mirror_data_lport ;
int dsock = netutils_init_socket ( & dport , use_ipv6 , 0 ) ;
if ( dsock = = - 1 ) {
goto sockets_cleanup ;
}
/* Listen to the data socket if using TCP */
if ( listen ( dsock , 1 ) < 0 ) {
goto sockets_cleanup ;
}
/* Set socket descriptors */
raop_rtp_mirror - > mirror_data_sock = dsock ;
/* Set port values */
raop_rtp_mirror - > mirror_data_lport = dport ;
logger_log ( raop_rtp_mirror - > logger , LOGGER_DEBUG , " raop_rtp_mirror local data port socket %d port TCP %d " , dsock , dport ) ;
return 0 ;
sockets_cleanup :
if ( dsock ! = - 1 ) closesocket ( dsock ) ;
return - 1 ;
}
2021-08-21 16:33:31 -04:00
2020-04-15 16:24:10 +03:00
void
2022-02-05 00:55:13 -05:00
raop_rtp_start_mirror ( raop_rtp_mirror_t * raop_rtp_mirror , int use_udp , unsigned short * mirror_data_lport , uint8_t show_client_FPS_data )
2020-04-15 16:24:10 +03:00
{
logger_log ( raop_rtp_mirror - > logger , LOGGER_INFO , " raop_rtp_mirror starting mirroring " ) ;
int use_ipv6 = 0 ;
assert ( raop_rtp_mirror ) ;
2021-12-04 13:13:16 -05:00
assert ( mirror_data_lport ) ;
2022-02-05 00:55:13 -05:00
raop_rtp_mirror - > show_client_FPS_data = show_client_FPS_data ;
2020-04-15 16:24:10 +03:00
MUTEX_LOCK ( raop_rtp_mirror - > run_mutex ) ;
if ( raop_rtp_mirror - > running | | ! raop_rtp_mirror - > joined ) {
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
return ;
}
if ( raop_rtp_mirror - > remote_saddr . ss_family = = AF_INET6 ) {
use_ipv6 = 1 ;
}
use_ipv6 = 0 ;
2021-10-13 09:05:37 -04:00
raop_rtp_mirror - > mirror_data_lport = * mirror_data_lport ;
2020-04-15 16:24:10 +03:00
if ( raop_rtp_init_mirror_sockets ( raop_rtp_mirror , use_ipv6 ) < 0 ) {
logger_log ( raop_rtp_mirror - > logger , LOGGER_ERR , " raop_rtp_mirror initializing sockets failed " ) ;
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
return ;
}
2021-12-04 13:13:16 -05:00
* mirror_data_lport = raop_rtp_mirror - > mirror_data_lport ;
2020-04-15 16:24:10 +03:00
/* Create the thread and initialize running values */
raop_rtp_mirror - > running = 1 ;
raop_rtp_mirror - > joined = 0 ;
THREAD_CREATE ( raop_rtp_mirror - > thread_mirror , raop_rtp_mirror_thread , raop_rtp_mirror ) ;
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
}
void raop_rtp_mirror_stop ( raop_rtp_mirror_t * raop_rtp_mirror ) {
assert ( raop_rtp_mirror ) ;
/* Check that we are running and thread is not
* joined ( should never be while still running ) */
MUTEX_LOCK ( raop_rtp_mirror - > run_mutex ) ;
if ( ! raop_rtp_mirror - > running | | raop_rtp_mirror - > joined ) {
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
return ;
}
raop_rtp_mirror - > running = 0 ;
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
if ( raop_rtp_mirror - > mirror_data_sock ! = - 1 ) {
closesocket ( raop_rtp_mirror - > mirror_data_sock ) ;
raop_rtp_mirror - > mirror_data_sock = - 1 ;
}
/* Join the thread */
THREAD_JOIN ( raop_rtp_mirror - > thread_mirror ) ;
/* Mark thread as joined */
MUTEX_LOCK ( raop_rtp_mirror - > run_mutex ) ;
raop_rtp_mirror - > joined = 1 ;
MUTEX_UNLOCK ( raop_rtp_mirror - > run_mutex ) ;
}
void raop_rtp_mirror_destroy ( raop_rtp_mirror_t * raop_rtp_mirror ) {
if ( raop_rtp_mirror ) {
raop_rtp_mirror_stop ( raop_rtp_mirror ) ;
MUTEX_DESTROY ( raop_rtp_mirror - > run_mutex ) ;
mirror_buffer_destroy ( raop_rtp_mirror - > buffer ) ;
2022-04-22 11:19:31 -04:00
if ( raop_rtp_mirror - > sps_pps ) {
free ( raop_rtp_mirror - > sps_pps ) ;
}
2022-02-05 00:55:13 -05:00
free ( raop_rtp_mirror ) ;
2020-04-15 16:24:10 +03:00
}
}