/***************************************************************************** * Entry point *****************************************************************************/ int main( int i_argc, char **pp_argv ) { int i, c; int i_priority = -1; int i_ttl = 0; struct pollfd *pfd = NULL; int i_fd; bool b_tcp; #define ADD_INPUT \ p_inputs = realloc( p_inputs, ++i_nb_inputs * sizeof(input_t) ); \ p_inputs[i_nb_inputs - 1].i_fd = i_fd; \ p_inputs[i_nb_inputs - 1].b_tcp = b_tcp; \ p_inputs[i_nb_inputs - 1].p_block = NULL; \ pfd = realloc( pfd, i_nb_inputs * sizeof(struct pollfd) ); \ pfd[i_nb_inputs - 1].fd = i_fd; \ pfd[i_nb_inputs - 1].events = POLLIN | POLLERR | POLLRDHUP | POLLHUP; while ( (c = getopt( i_argc, pp_argv, "i:t:b:g:j:r:n:x:X:Um:R:h" )) != -1 ) { switch ( c ) { case 'i': i_priority = strtol( optarg, NULL, 0 ); break; case 't': i_ttl = strtol( optarg, NULL, 0 ); break; case 'b': i_buffer_length = strtoll( optarg, NULL, 0 ) * 27000; break; case 'g': i_cr_max_gap = strtoll( optarg, NULL, 0 ) * 27000; break; case 'j': i_cr_max_jitter = strtoll( optarg, NULL, 0 ) * 27000; break; case 'r': i_cr_average = strtol( optarg, NULL, 0 ); break; case 'n': i_max_retx_burst = strtoul( optarg, NULL, 0 ); break; case 'x': i_retx_delay = strtoll( optarg, NULL, 0 ) * 27000; break; case 'X': i_retx_fd = i_fd = OpenSocket( optarg, 0, 0, 0, NULL, &b_tcp, NULL ); if ( i_fd == -1 ) { msg_Err( NULL, "unable to set up retx with %s\n", optarg ); exit(EXIT_FAILURE); } ADD_INPUT break; case 'U': b_udp = 1; break; case 'm': i_asked_payload_size = strtol( optarg, NULL, 0 ); break; case 'R': i_rtp_header_size = strtol( optarg, NULL, 0 ); break; case 'h': default: usage(); break; } } if ( optind >= i_argc - 1 ) usage(); while ( optind < i_argc - 1 ) { i_fd = OpenSocket( pp_argv[optind], 0, DEFAULT_PORT, 0, NULL, &b_tcp, NULL ); if ( i_fd == -1 ) { msg_Err( NULL, "unable to open input %s\n", pp_argv[optind] ); exit(EXIT_FAILURE); } optind++; ADD_INPUT } msg_Dbg( NULL, "%d inputs", i_nb_inputs ); i_nb_retx = (i_buffer_length - MIN_RETX_DELAY) / i_retx_delay; pp_retx = malloc( i_nb_retx * sizeof(block_t *) ); for ( i = 0; i < i_nb_retx; i++ ) pp_retx[i] = NULL; if ( i_retx_fd && i_nb_retx ) msg_Dbg( NULL, "%d retx passes", i_nb_retx ); i_output_fd = OpenSocket( pp_argv[optind], i_ttl, 0, DEFAULT_PORT, NULL, NULL, NULL ); if ( i_output_fd == -1 ) { msg_Err( NULL, "unable to open output %s\n", pp_argv[optind] ); exit(EXIT_FAILURE); } clock_Init(); if ( i_priority > 0 ) { struct sched_param param; int i_error; memset( ¶m, 0, sizeof(struct sched_param) ); param.sched_priority = i_priority; if ( (i_error = pthread_setschedparam( pthread_self(), SCHED_RR, ¶m )) ) { msg_Warn( NULL, "couldn't set thread priority: %s", strerror(i_error) ); } } for ( ; ; ) { int i_timeout = -1; uint64_t i_current_date; while ( p_first != NULL && p_first->i_date <= (i_current_date = wall_Date()) + 26999 ) PacketSend(); i_current_date = wall_Date(); RetxCheck( i_current_date ); i_current_date = wall_Date(); if ( p_first != NULL ) i_timeout = (p_first->i_date - i_current_date) / 27000; if ( poll( pfd, i_nb_inputs, i_timeout ) < 0 ) { int saved_errno = errno; msg_Warn( NULL, "couldn't poll(): %s", strerror(errno) ); if ( saved_errno == EINTR ) continue; exit(EXIT_FAILURE); } i_current_date = wall_Date(); for ( i = 0; i < i_nb_inputs; i++ ) { input_t *p_input = &p_inputs[i]; if ( pfd[i].revents & POLLIN ) { ssize_t i_size = i_asked_payload_size + i_rtp_header_size; uint8_t *p_buffer; if ( p_input->p_block == NULL ) { p_input->p_block = malloc( sizeof(block_t) + i_size ); p_buffer = p_input->p_block->p_data = (uint8_t *)p_input->p_block + sizeof(block_t); p_input->p_block->i_size = 0; } else { p_buffer = p_input->p_block->p_data + p_input->p_block->i_size; i_size -= p_input->p_block->i_size; } i_size = read( p_input->i_fd, p_buffer, i_size ); if ( i_size < 0 && errno != EAGAIN && errno != EINTR && errno != ECONNREFUSED ) { msg_Err( NULL, "unrecoverable read error, dying (%s)", strerror(errno) ); exit(EXIT_FAILURE); } if ( i_size <= 0 ) continue; p_input->p_block->i_size += i_size; if ( p_input->b_tcp && p_input->p_block->i_size != i_asked_payload_size + i_rtp_header_size ) continue; if ( i_retx_fd == -1 || i ) PacketRecv( p_input->p_block, i_current_date ); else PacketRecv( p_input->p_block, 0 ); p_input->p_block = NULL; } else if ( pfd[i].revents & (POLLERR | POLLRDHUP | POLLHUP) ) { msg_Err( NULL, "poll error on input %d" ); exit(EXIT_FAILURE); } } } return EXIT_SUCCESS; }
/***************************************************************************** * Entry point *****************************************************************************/ int main( int i_argc, char **pp_argv ) { int c; int i_priority = -1; int i_ttl = 0; bool b_udp = false; struct pollfd pfd[2]; while ( (c = getopt( i_argc, pp_argv, "i:t:wo:x:X:Um:R:h" )) != -1 ) { switch ( c ) { case 'i': i_priority = strtol( optarg, NULL, 0 ); break; case 't': i_ttl = strtol( optarg, NULL, 0 ); break; case 'w': b_overwrite_timestamps = true; break; case 'o': { struct in_addr maddr; if ( !inet_aton( optarg, &maddr ) ) usage(); i_ssrc = maddr.s_addr; b_overwrite_ssrc = true; break; } case 'x': i_retx_buffer = strtoll( optarg, NULL, 0 ) * 27000; break; case 'X': i_retx_fd = OpenSocket( optarg, 0, 0, 0, NULL, &b_retx_tcp, NULL ); if ( i_retx_fd == -1 ) { msg_Err( NULL, "unable to set up retx with %s\n", optarg ); exit(EXIT_FAILURE); } pfd[1].fd = i_retx_fd; pfd[1].events = POLLIN | POLLERR | POLLRDHUP | POLLHUP; p_retx_block = malloc( sizeof(block_t) + RETX_HEADER_SIZE ); p_retx_block->p_data = (uint8_t *)p_retx_block + sizeof(block_t); p_retx_block->i_size = 0; break; case 'U': b_udp = true; break; case 'm': i_asked_payload_size = strtol( optarg, NULL, 0 ); break; case 'R': i_rtp_header_size = strtol( optarg, NULL, 0 ); break; case 'h': default: usage(); break; } } if ( optind >= i_argc - 1 ) usage(); i_input_fd = OpenSocket( pp_argv[optind], 0, DEFAULT_PORT, 0, NULL, &b_input_tcp, NULL ); if ( i_input_fd == -1 ) { msg_Err( NULL, "unable to open input socket" ); exit(EXIT_FAILURE); } optind++; pfd[0].fd = i_input_fd; pfd[0].events = POLLIN | POLLERR | POLLRDHUP | POLLHUP; while ( optind < i_argc ) { p_outputs = realloc( p_outputs, ++i_nb_outputs * sizeof(output_t) ); p_outputs[i_nb_outputs - 1].i_fd = OpenSocket( pp_argv[optind++], i_ttl, 0, DEFAULT_PORT, &p_outputs[i_nb_outputs - 1].i_weight, NULL, NULL ); if ( p_outputs[i_nb_outputs - 1].i_fd == -1 ) { msg_Err( NULL, "unable to open output socket" ); exit(EXIT_FAILURE); } p_outputs[i_nb_outputs - 1].i_weighted_size = p_outputs[i_nb_outputs - 1].i_remainder = 0; i_max_weight += p_outputs[i_nb_outputs - 1].i_weight; } msg_Dbg( NULL, "%d outputs weight %u%s", i_nb_outputs, i_max_weight, i_retx_fd != -1 ? ", with retx" : "" ); if ( i_priority > 0 ) { struct sched_param param; int i_error; memset( ¶m, 0, sizeof(struct sched_param) ); param.sched_priority = i_priority; if ( (i_error = pthread_setschedparam( pthread_self(), SCHED_RR, ¶m )) ) { msg_Warn( NULL, "couldn't set thread priority: %s", strerror(i_error) ); } } for ( ; ; ) { uint64_t i_current_date; if ( poll( pfd, i_retx_fd == -1 ? 1 : 2, -1 ) < 0 ) { int saved_errno = errno; msg_Warn( NULL, "couldn't poll(): %s", strerror(errno) ); if ( saved_errno == EINTR ) continue; exit(EXIT_FAILURE); } i_current_date = wall_Date(); if ( (pfd[0].revents & (POLLERR | POLLRDHUP | POLLHUP)) || (i_retx_fd != -1 && (pfd[1].revents & (POLLERR | POLLRDHUP | POLLHUP)))) { msg_Err( NULL, "poll error\n" ); exit(EXIT_FAILURE); } if ( pfd[0].revents & POLLIN ) { /* Read input block */ ssize_t i_size, i_wanted_size; uint8_t *p_read_buffer; if ( b_udp ) i_wanted_size = i_asked_payload_size + RTP_HEADER_SIZE; else i_wanted_size = i_asked_payload_size + i_rtp_header_size; if ( p_input_block == NULL ) { if ( b_udp ) { p_input_block = malloc( sizeof(block_t) + i_asked_payload_size + RTP_HEADER_SIZE ); p_input_block->i_size = RTP_HEADER_SIZE; } else { p_input_block = malloc( sizeof(block_t) + i_asked_payload_size + i_rtp_header_size ); p_input_block->p_data = (uint8_t *)p_input_block + sizeof(block_t); p_input_block->i_size = 0; } p_input_block->p_data = (uint8_t *)p_input_block + sizeof(block_t); } p_read_buffer = p_input_block->p_data + p_input_block->i_size; i_wanted_size -= p_input_block->i_size; i_size = read( i_input_fd, p_read_buffer, i_wanted_size ); if ( i_size < 0 && errno != EAGAIN && errno != EINTR ) { msg_Err( NULL, "unrecoverable read error, dying (%s)", strerror(errno) ); exit(EXIT_FAILURE); } if ( i_size <= 0 ) continue; p_input_block->i_size += i_size; if ( b_input_tcp && i_size != i_wanted_size ) continue; if ( b_udp ) { rtp_set_hdr( p_input_block->p_data ); rtp_set_type( p_input_block->p_data, RTP_TYPE_TS ); rtp_set_seqnum( p_input_block->p_data, i_rtp_seqnum ); i_rtp_seqnum++; rtp_set_ssrc( p_input_block->p_data, (uint8_t *)&i_ssrc ); /* this isn't RFC-compliant, but we assume that at the other * end, the RTP header will be stripped */ rtp_set_timestamp( p_input_block->p_data, i_current_date / 300 ); } else { if ( b_overwrite_ssrc ) rtp_set_ssrc( p_input_block->p_data, (uint8_t *)&i_ssrc ); if ( b_overwrite_timestamps ) rtp_set_timestamp( p_input_block->p_data, i_current_date / 300 ); } /* Output block */ output_t *p_output = NextOutput(); SendBlock( p_output->i_fd, NULL, 0, p_input_block ); p_output->i_weighted_size += (i_size + p_output->i_remainder) / p_output->i_weight; p_output->i_remainder = (i_size + p_output->i_remainder) % p_output->i_weight; if ( i_retx_fd != -1 ) RetxQueue( p_input_block, i_current_date ); else free( p_input_block ); p_input_block = NULL; } if ( i_retx_fd != -1 && (pfd[1].revents & POLLIN) ) RetxHandle(); } return EXIT_SUCCESS; }