/***************************************************************************** * output_Flush *****************************************************************************/ static void output_Flush( output_t *p_output ) { packet_t *p_packet = p_output->p_packets; int i_block_cnt = output_BlockCount( p_output ); struct iovec p_iov[i_block_cnt + 2]; uint8_t p_rtp_hdr[RTP_HEADER_SIZE]; int i_iov = 0, i_payload_len, i_block; if ( (p_output->config.i_config & OUTPUT_RAW) ) { p_iov[i_iov].iov_base = &p_output->raw_pkt_header; p_iov[i_iov].iov_len = sizeof(struct udprawpkt); i_iov++; } if ( !(p_output->config.i_config & OUTPUT_UDP) ) { p_iov[i_iov].iov_base = p_rtp_hdr; p_iov[i_iov].iov_len = sizeof(p_rtp_hdr); rtp_set_hdr( p_rtp_hdr ); rtp_set_type( p_rtp_hdr, RTP_TYPE_TS ); rtp_set_seqnum( p_rtp_hdr, p_output->i_seqnum++ ); rtp_set_timestamp( p_rtp_hdr, p_output->i_ref_timestamp + (p_packet->i_dts - p_output->i_ref_wallclock) * 9 / 100 ); rtp_set_ssrc( p_rtp_hdr, p_output->config.pi_ssrc ); i_iov++; } for ( i_block = 0; i_block < p_packet->i_depth; i_block++ ) { /* Do pid mapping here if needed. * save the original pid in the block. * set the pid to the new pid * later we re-instate the old pid for the next output */ if ( b_do_remap || p_output->b_do_remap ) { block_t *p_block = p_packet->pp_blocks[i_block]; uint16_t i_pid = ts_get_pid( p_block->p_ts ); p_block->tmp_pid = UNUSED_PID; if ( p_output->pi_newpids[i_pid] != UNUSED_PID ) { uint16_t i_newpid = p_output->pi_newpids[i_pid]; /* Need to map this pid to the new pid */ ts_set_pid( p_block->p_ts, i_newpid ); p_block->tmp_pid = i_pid; } } p_iov[i_iov].iov_base = p_packet->pp_blocks[i_block]->p_ts; p_iov[i_iov].iov_len = TS_SIZE; i_iov++; } for ( ; i_block < i_block_cnt; i_block++ ) { p_iov[i_iov].iov_base = p_pad_ts; p_iov[i_iov].iov_len = TS_SIZE; i_iov++; } if ( (p_output->config.i_config & OUTPUT_RAW) ) { i_payload_len = 0; for ( i_block = 1; i_block < i_iov; i_block++ ) { i_payload_len += p_iov[i_block].iov_len; } p_output->raw_pkt_header.udph.len = htons(sizeof(struct udpheader) + i_payload_len); } if ( writev( p_output->i_handle, p_iov, i_iov ) < 0 ) { msg_Err( NULL, "couldn't writev to %s (%s)", p_output->config.psz_displayname, strerror(errno) ); } /* Update the wallclock because writev() can take some time. */ i_wallclock = mdate(); for ( i_block = 0; i_block < p_packet->i_depth; i_block++ ) { p_packet->pp_blocks[i_block]->i_refcount--; if ( !p_packet->pp_blocks[i_block]->i_refcount ) block_Delete( p_packet->pp_blocks[i_block] ); else if ( b_do_remap || p_output->b_do_remap ) { /* still referenced so re-instate the orignial pid if remapped */ block_t * p_block = p_packet->pp_blocks[i_block]; if (p_block->tmp_pid != UNUSED_PID) ts_set_pid( p_block->p_ts, p_block->tmp_pid ); } } p_output->p_packets = p_packet->p_next; free( p_packet ); if ( p_output->p_packets == NULL ) p_output->p_last_packet = NULL; }
int main(int argc, char *argv[]) { #ifndef RTPESND char p_header[PIPE_HEADER_LEN]; #endif RTPSEND char r_header[RTP_HEADER_LEN]; char *buffer; struct iovec iov[3]; ssize_t length; u_int16_t seq; PIPE_CONTEXT p; int dummy_cnt; /* dummy(very old packet)cnt */ #ifdef RTPSEND int count=0; /* for Debug */ struct timeval blk_start_tv; int blk_wait4start=1; int blk_pktcnt = 0; u_int32_t waitperframe; #endif sigset_t sigset; ssize_t io_ret; /* sigfillset(&sigset); */ /* sigdelset(&sigset,SIGINT); */ /* sigdelset(&sigset,SIGTSTP); */ if (sigprocmask(SIG_BLOCK,&sigset,NULL)!=0){ e_printf("sigprocmask fail.\n"); exit(0); } signal(SIGINT,cb_sig); signal(SIGTSTP,cb_sig); signal(SIGPIPE,cb_sig); if (opt_etc(argc, argv) == -1) { return -1; } if (isatty(STDIN)) { e_printf("Standard input must be binded with pipe.\n"); return -1; } #ifndef RTPSEND if (isatty(STDOUT)) { e_printf("Standard output must be binded with pipe.\n"); return -1; } #endif /* ! RTPSEND */ if ((buffer = malloc(OPT.plen)) == NULL) { e_printf("cannot malloc for buffer\n"); return -1; } if (OPT.compatible_mode) { seq = 0; } else { srand(time(NULL)); seq = rand() & 0xffff; } d1_printf("first seq/rtp=%u\n",seq); rtp_reset(r_header, RTP_HEADER_LEN); rtp_set_version(r_header, 1); rtp_set_ptype(r_header, OPT.payload_type); memset(buffer, 0, OPT.plen); #ifdef RTPSEND switch(OPT.shaping_lev) { case 0: default: OPT.shaping_lev=0; waitperframe=1; d1_printf("shaping_mode : frame\n"); break; case 1: waitperframe=OPT.rs_N; d1_printf("shaping_mode : packet with dups\n"); break; case 2: waitperframe=OPT.rs_N*OPT.send_double; d1_printf("shaping_mode : all packet(include dups)\n"); break; } iov[0].iov_base = r_header; iov[0].iov_len = RTP_HEADER_LEN; iov[1].iov_base = buffer; /* iov[1].iov_len = OPT.plen;*/ #else /* RTPSEND */ iov[0].iov_base = p_header; iov[0].iov_len = PIPE_HEADER_LEN; iov[1].iov_base = r_header; iov[1].iov_len = RTP_HEADER_LEN; iov[2].iov_base = buffer; /* iov[2].iov_len = OPT.plen;*/ #endif /* RTPSEND */ p=pipe_context_init(STDIN,1 /*OPT.rs_N*/, OPT.plen); wclk_ps_tvstart.tv_sec=0; while ((length = pipe_blocked_read_packet_ex(p, p_header, buffer))>=0){ #ifdef RTPSEND if(wclk_ps_tvstart.tv_sec==0){ /* recode start time */ gettimeofday(&wclk_ps_tvstart, NULL); } count++; if(blk_wait4start){ gettimeofday(&blk_start_tv,NULL); blk_wait4start=0; blk_pktcnt = 0; }else{ blk_pktcnt++; } blk_wait4start=pipe_get_marker(p_header); #endif #ifdef RTPSEND iov[1].iov_len =length; pipe_set_length(p_header,length); #else iov[2].iov_len =length; pipe_set_length(p_header,length + RTP_HEADER_LEN); #endif /* RTPSEND*/ rtp_set_timestamp(r_header, pipe_get_timestamp(p_header)); rtp_set_marker(r_header, pipe_get_marker(p_header)); rtp_set_seqnum(r_header, seq++); if(seq>=0x10000) seq-=0x10000; d3_printf("rtpenc: marker %d : seq %d\n",pipe_get_marker(p_header),seq-1); #ifdef RTPSEND if (display_time_all_packet) { d_printf("\n%d:", seq); } rtsend_tslimit+=(u_int32_t)(pipe_get_timestamp(p_header) - pkt_tslast); pkt_tslast=pipe_get_timestamp(p_header); #if 0 wait4rtsend(&wclk_tvstart,blk_pktcnt); #else if(!(OPT.shaping_lev==0 && blk_pktcnt>=1)){ wait4rtsend(&wclk_tvstart,&rtsend_tslimit,blk_pktcnt,waitperframe); } #endif if (OPT.send_err_rate > 0) { if ((rand() % OPT.send_err_rate) == 0) { total_drop++; continue; } } d2_printf(" write %d bytes", pipe_get_length(p_header)); if (writev(OPT.sfd, iov, 2) == -1) { total_drop++; d2_printf(" writev-NG(ts/sq=%d/%d)\n", pipe_get_timestamp(p_header), count); } else { d2_printf(" writev-OK(ts/sq=%d/%d)\n", pipe_get_timestamp(p_header), count); total_send++; } if(OPT.dup2dummy){ rtp_set_seqnum(r_header, (seq+0x10000-0x100)&0xffff); } for(dummy_cnt=1;dummy_cnt<OPT.send_double;dummy_cnt++){ if(OPT.shaping_lev==2){ blk_pktcnt++; wait4rtsend(&wclk_tvstart,&rtsend_tslimit,blk_pktcnt,waitperframe); } d2_printf(" write dup(%d/%d) \n",dummy_cnt,OPT.send_double); io_ret=writev(OPT.sfd, iov, 2); if ( io_ret == -1) { total_drop++; d2_printf(" writev-NG(ts/sq=%d/%d)\n", pipe_get_timestamp(p_header), count); } else { d2_printf(" writev-OK(ts/sq=%d/%d)\n", pipe_get_timestamp(p_header), count); total_send++; } } #else /* RTPSEND */ d2_printf(" write pkt\n"); writev(STDOUT, iov, 3); if(OPT.dup2dummy){ rtp_set_seqnum(r_header, (seq+0x10000-0x100)&0xffff); } for(dummy_cnt=1;dummy_cnt<OPT.send_double;dummy_cnt++){ d2_printf(" write dup(%d/%d) \n",dummy_cnt,OPT.send_double); io_ret=writev(STDOUT, iov, 3); if(io_ret==-1){ exit_req=1; break; } } #endif /* RTPSEND */ if(exit_req==1){ break; } } #ifdef RTPSEND last_seq=seq-1; statistics_display(); sleep(1); #endif return 0; }
/***************************************************************************** * 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; }