static void *start_smoothing( void *ptr ) { obe_t *h = ptr; int num_enc_smoothing_frames = 0, buffer_frames = 0; int64_t start_dts = -1, start_pts = -1, last_clock = -1; obe_coded_frame_t *coded_frame = NULL; struct sched_param param = {0}; param.sched_priority = 99; pthread_setschedparam( pthread_self(), SCHED_FIFO, ¶m ); /* FIXME: when we have soft pulldown this will need changing */ if( h->obe_system == OBE_SYSTEM_TYPE_GENERIC ) { for( int i = 0; i < h->num_encoders; i++ ) { if( h->encoders[i]->is_video ) { pthread_mutex_lock( &h->encoders[i]->queue.mutex ); while( !h->encoders[i]->is_ready ) pthread_cond_wait( &h->encoders[i]->queue.in_cv, &h->encoders[i]->queue.mutex ); x264_param_t *params = h->encoders[i]->encoder_params; buffer_frames = params->sc.i_buffer_size; pthread_mutex_unlock( &h->encoders[i]->queue.mutex ); break; } } } int64_t send_delta = 0; while( 1 ) { pthread_mutex_lock( &h->enc_smoothing_queue.mutex ); if( h->cancel_enc_smoothing_thread ) { pthread_mutex_unlock( &h->enc_smoothing_queue.mutex ); break; } while( h->enc_smoothing_queue.size == num_enc_smoothing_frames ) pthread_cond_wait( &h->enc_smoothing_queue.in_cv, &h->enc_smoothing_queue.mutex ); if( h->cancel_enc_smoothing_thread ) { pthread_mutex_unlock( &h->enc_smoothing_queue.mutex ); break; } num_enc_smoothing_frames = h->enc_smoothing_queue.size; if( !h->enc_smoothing_buffer_complete ) { if( num_enc_smoothing_frames >= buffer_frames ) { h->enc_smoothing_buffer_complete = 1; start_dts = -1; } else { pthread_mutex_unlock( &h->enc_smoothing_queue.mutex ); continue; } } // printf("\n smoothed frames %i \n", num_enc_smoothing_frames ); coded_frame = h->enc_smoothing_queue.queue[0]; pthread_mutex_unlock( &h->enc_smoothing_queue.mutex ); /* The terminology can be a cause for confusion: * pts refers to the pts from the input which is monotonic * dts refers to the dts out of the encoder which is monotonic */ pthread_mutex_lock( &h->obe_clock_mutex ); //printf("\n dts gap %"PRIi64" \n", coded_frame->real_dts - start_dts ); //printf("\n pts gap %"PRIi64" \n", h->obe_clock_last_pts - start_pts ); last_clock = h->obe_clock_last_pts; if( start_dts == -1 ) { start_dts = coded_frame->real_dts; /* Wait until the next clock tick */ while( last_clock == h->obe_clock_last_pts ) pthread_cond_wait( &h->obe_clock_cv, &h->obe_clock_mutex ); start_pts = h->obe_clock_last_pts; } else if( coded_frame->real_dts - start_dts > h->obe_clock_last_pts - start_pts ) { //printf("\n waiting \n"); while( last_clock == h->obe_clock_last_pts ) pthread_cond_wait( &h->obe_clock_cv, &h->obe_clock_mutex ); } /* otherwise, continue since the frame is late */ pthread_mutex_unlock( &h->obe_clock_mutex ); add_to_queue( &h->mux_queue, coded_frame ); //printf("\n send_delta %"PRIi64" \n", get_input_clock_in_mpeg_ticks( h ) - send_delta ); //send_delta = get_input_clock_in_mpeg_ticks( h ); remove_from_queue( &h->enc_smoothing_queue ); pthread_mutex_lock( &h->enc_smoothing_queue.mutex ); h->enc_smoothing_last_exit_time = get_input_clock_in_mpeg_ticks( h ); pthread_mutex_unlock( &h->enc_smoothing_queue.mutex ); num_enc_smoothing_frames = 0; } return NULL; }
static void *start_smoothing( void *ptr ) { obe_t *h = ptr; int num_muxed_data = 0, buffer_complete = 0; int64_t start_clock = -1, start_pcr, end_pcr, temporal_vbv_size = 0, cur_pcr; obe_muxed_data_t **muxed_data = NULL, *start_data, *end_data; AVFifoBuffer *fifo_data = NULL, *fifo_pcr = NULL; uint8_t *output_buf; struct sched_param param = {0}; param.sched_priority = 99; pthread_setschedparam( pthread_self(), SCHED_FIFO, ¶m ); /* This thread buffers one VBV worth of frames */ fifo_data = av_fifo_alloc( TS_PACKETS_SIZE ); if( !fifo_data ) { fprintf( stderr, "[mux-smoothing] Could not allocate data fifo" ); return NULL; } fifo_pcr = av_fifo_alloc( 7 * sizeof(int64_t) ); if( !fifo_pcr ) { fprintf( stderr, "[mux-smoothing] Could not allocate pcr fifo" ); return NULL; } if( h->obe_system == OBE_SYSTEM_TYPE_GENERIC ) { for( int i = 0; i < h->num_encoders; i++ ) { if( h->encoders[i]->is_video ) { pthread_mutex_lock( &h->encoders[i]->queue.mutex ); while( !h->encoders[i]->is_ready ) pthread_cond_wait( &h->encoders[i]->queue.in_cv, &h->encoders[i]->queue.mutex ); x264_param_t *params = h->encoders[i]->encoder_params; temporal_vbv_size = av_rescale_q_rnd( (int64_t)params->rc.i_vbv_buffer_size * params->rc.f_vbv_buffer_init, (AVRational){1, params->rc.i_vbv_max_bitrate }, (AVRational){ 1, OBE_CLOCK }, AV_ROUND_UP ); pthread_mutex_unlock( &h->encoders[i]->queue.mutex ); break; } } } while( 1 ) { pthread_mutex_lock( &h->mux_smoothing_queue.mutex ); while( h->mux_smoothing_queue.size == num_muxed_data && !h->cancel_mux_smoothing_thread ) pthread_cond_wait( &h->mux_smoothing_queue.in_cv, &h->mux_smoothing_queue.mutex ); if( h->cancel_mux_smoothing_thread ) { pthread_mutex_unlock( &h->mux_smoothing_queue.mutex ); break; } num_muxed_data = h->mux_smoothing_queue.size; /* Refill the buffer after a drop */ pthread_mutex_lock( &h->drop_mutex ); if( h->mux_drop ) { syslog( LOG_INFO, "Mux smoothing buffer reset\n" ); h->mux_drop = 0; av_fifo_reset( fifo_data ); av_fifo_reset( fifo_pcr ); buffer_complete = 0; start_clock = -1; } pthread_mutex_unlock( &h->drop_mutex ); if( !buffer_complete ) { start_data = h->mux_smoothing_queue.queue[0]; end_data = h->mux_smoothing_queue.queue[num_muxed_data-1]; start_pcr = start_data->pcr_list[0]; end_pcr = end_data->pcr_list[(end_data->len / 188)-1]; if( end_pcr - start_pcr >= temporal_vbv_size ) { buffer_complete = 1; start_clock = -1; } else { pthread_mutex_unlock( &h->mux_smoothing_queue.mutex ); continue; } } //printf("\n mux smoothed frames %i \n", num_muxed_data ); muxed_data = malloc( num_muxed_data * sizeof(*muxed_data) ); if( !muxed_data ) { pthread_mutex_unlock( &h->output_queue.mutex ); syslog( LOG_ERR, "Malloc failed\n" ); return NULL; } memcpy( muxed_data, h->mux_smoothing_queue.queue, num_muxed_data * sizeof(*muxed_data) ); pthread_mutex_unlock( &h->mux_smoothing_queue.mutex ); for( int i = 0; i < num_muxed_data; i++ ) { if( av_fifo_realloc2( fifo_data, av_fifo_size( fifo_data ) + muxed_data[i]->len ) < 0 ) { syslog( LOG_ERR, "Malloc failed\n" ); return NULL; } av_fifo_generic_write( fifo_data, muxed_data[i]->data, muxed_data[i]->len, NULL ); if( av_fifo_realloc2( fifo_pcr, av_fifo_size( fifo_pcr ) + ((muxed_data[i]->len * sizeof(int64_t)) / 188) ) < 0 ) { syslog( LOG_ERR, "Malloc failed\n" ); return NULL; } av_fifo_generic_write( fifo_pcr, muxed_data[i]->pcr_list, (muxed_data[i]->len * sizeof(int64_t)) / 188, NULL ); remove_from_queue( &h->mux_smoothing_queue ); destroy_muxed_data( muxed_data[i] ); } free( muxed_data ); muxed_data = NULL; num_muxed_data = 0; while( av_fifo_size( fifo_data ) >= TS_PACKETS_SIZE ) { output_buf = malloc( TS_PACKETS_SIZE + 7 * sizeof(int64_t) ); if( !output_buf ) { syslog( LOG_ERR, "Malloc failed\n" ); return NULL; } av_fifo_generic_read( fifo_pcr, output_buf, 7 * sizeof(int64_t), NULL ); av_fifo_generic_read( fifo_data, &output_buf[7 * sizeof(int64_t)], TS_PACKETS_SIZE, NULL ); cur_pcr = AV_RN64( output_buf ); if( start_clock != -1 ) { sleep_input_clock( h, cur_pcr - start_pcr + start_clock ); } if( start_clock == -1 ) { start_clock = get_input_clock_in_mpeg_ticks( h ); start_pcr = cur_pcr; } if( add_to_queue( &h->output_queue, output_buf ) < 0 ) return NULL; output_buf = NULL; } } av_fifo_free( fifo_data ); av_fifo_free( fifo_pcr ); return NULL; }
static void *start_encoder( void *ptr ) { obe_vid_enc_params_t *enc_params = ptr; obe_t *h = enc_params->h; obe_encoder_t *encoder = enc_params->encoder; x264_t *s = NULL; x264_picture_t pic, pic_out; x264_nal_t *nal; int i_nal, frame_size = 0, user_sar_width, user_sar_height; int64_t pts = 0, arrival_time = 0, frame_duration, buffer_duration; int64_t *pts2; float buffer_fill; obe_raw_frame_t *raw_frame; obe_coded_frame_t *coded_frame; /* TODO: check for width, height changes */ /* Lock the mutex until we verify and fetch new parameters */ pthread_mutex_lock( &encoder->encoder_mutex ); enc_params->avc_param.pf_log = x264_logger; s = x264_encoder_open( &enc_params->avc_param ); if( !s ) { pthread_mutex_unlock( &encoder->encoder_mutex ); fprintf( stderr, "[x264]: encoder configuration failed\n" ); goto end; } x264_encoder_parameters( s, &enc_params->avc_param ); encoder->encoder_params = malloc( sizeof(enc_params->avc_param) ); if( !encoder->encoder_params ) { pthread_mutex_unlock( &encoder->encoder_mutex ); syslog( LOG_ERR, "Malloc failed\n" ); goto end; } memcpy( encoder->encoder_params, &enc_params->avc_param, sizeof(enc_params->avc_param) ); encoder->is_ready = 1; /* XXX: This will need fixing for soft pulldown streams */ frame_duration = av_rescale_q( 1, (AVRational){enc_params->avc_param.i_fps_den, enc_params->avc_param.i_fps_num}, (AVRational){1, OBE_CLOCK} ); buffer_duration = frame_duration * enc_params->avc_param.sc.i_buffer_size; /* Broadcast because input and muxer can be stuck waiting for encoder */ pthread_cond_broadcast( &encoder->encoder_cv ); pthread_mutex_unlock( &encoder->encoder_mutex ); user_sar_width = enc_params->avc_param.vui.i_sar_width; user_sar_height = enc_params->avc_param.vui.i_sar_height; while( 1 ) { pthread_mutex_lock( &encoder->encoder_mutex ); if( encoder->cancel_thread ) { pthread_mutex_unlock( &encoder->encoder_mutex ); break; } if( !encoder->num_raw_frames ) pthread_cond_wait( &encoder->encoder_cv, &encoder->encoder_mutex ); if( encoder->cancel_thread ) { pthread_mutex_unlock( &encoder->encoder_mutex ); break; } /* Reset the speedcontrol buffer if the source has dropped frames. Otherwise speedcontrol * stays in an underflow state and is locked to the fastest preset */ pthread_mutex_lock( &h->drop_mutex ); if( h->encoder_drop ) { pthread_mutex_lock( &h->smoothing_mutex ); h->smoothing_buffer_complete = 0; pthread_mutex_unlock( &h->smoothing_mutex ); syslog( LOG_INFO, "Speedcontrol reset\n" ); x264_speedcontrol_sync( s, enc_params->avc_param.sc.i_buffer_size, enc_params->avc_param.sc.f_buffer_init, 0 ); h->encoder_drop = 0; } pthread_mutex_unlock( &h->drop_mutex ); raw_frame = encoder->frames[0]; pthread_mutex_unlock( &encoder->encoder_mutex ); if( convert_obe_to_x264_pic( &pic, raw_frame ) < 0 ) { syslog( LOG_ERR, "Malloc failed\n" ); break; } /* FIXME: if frames are dropped this might not be true */ pic.i_pts = pts++; pts2 = malloc( sizeof(int64_t) ); if( !pts2 ) { syslog( LOG_ERR, "Malloc failed\n" ); break; } pts2[0] = raw_frame->pts; pic.opaque = pts2; /* If the AFD has changed, then change the SAR. x264 will write the SAR at the next keyframe * TODO: allow user to force keyframes in order to be frame accurate */ if( raw_frame->sar_width != enc_params->avc_param.vui.i_sar_width || raw_frame->sar_height != enc_params->avc_param.vui.i_sar_height ) { /* If the frame's SAR has been guessed but the user entered a reasonable SAR, then use it. * Otherwise, use the guessed SAR. */ if( raw_frame->sar_guess && user_sar_width > 0 && user_sar_height > 0 ) { enc_params->avc_param.vui.i_sar_width = user_sar_width; enc_params->avc_param.vui.i_sar_height = user_sar_height; } else { enc_params->avc_param.vui.i_sar_width = raw_frame->sar_width; enc_params->avc_param.vui.i_sar_height = raw_frame->sar_height; } x264_encoder_reconfig( s, &enc_params->avc_param ); } /* Update speedcontrol based on the system state */ if( h->obe_system == OBE_SYSTEM_TYPE_GENERIC ) { pthread_mutex_lock( &h->smoothing_mutex ); if( h->smoothing_buffer_complete ) { /* Wait until a frame is sent out. */ while( !h->smoothing_last_exit_time ) pthread_cond_wait( &h->smoothing_out_cv, &h->smoothing_mutex ); /* time elapsed since last frame was removed */ int64_t last_frame_delta = get_input_clock_in_mpeg_ticks( h ) - h->smoothing_last_exit_time; if( h->num_smoothing_frames ) { int64_t frame_durations = h->smoothing_frames[h->num_smoothing_frames-1]->real_dts - h->smoothing_frames[0]->real_dts + frame_duration; buffer_fill = (float)(frame_durations - last_frame_delta)/buffer_duration; } else buffer_fill = (float)(-1 * last_frame_delta)/buffer_duration; x264_speedcontrol_sync( s, buffer_fill, enc_params->avc_param.sc.i_buffer_size, 1 ); } pthread_mutex_unlock( &h->smoothing_mutex ); } frame_size = x264_encoder_encode( s, &nal, &i_nal, &pic, &pic_out ); arrival_time = raw_frame->arrival_time; raw_frame->release_data( raw_frame ); raw_frame->release_frame( raw_frame ); remove_frame_from_encode_queue( encoder ); if( frame_size < 0 ) { syslog( LOG_ERR, "x264_encoder_encode failed\n" ); break; } if( frame_size ) { coded_frame = new_coded_frame( encoder->stream_id, frame_size ); if( !coded_frame ) { syslog( LOG_ERR, "Malloc failed\n" ); break; } memcpy( coded_frame->data, nal[0].p_payload, frame_size ); coded_frame->is_video = 1; coded_frame->len = frame_size; coded_frame->cpb_initial_arrival_time = pic_out.hrd_timing.safe_cpb_initial_arrival_time; coded_frame->cpb_final_arrival_time = pic_out.hrd_timing.cpb_final_arrival_time; coded_frame->real_dts = pic_out.hrd_timing.cpb_removal_time; coded_frame->real_pts = pic_out.hrd_timing.dpb_output_time; pts2 = pic_out.opaque; coded_frame->pts = pts2[0]; coded_frame->random_access = pic_out.b_keyframe; coded_frame->priority = IS_X264_TYPE_I( pic_out.i_type ); free( pic_out.opaque ); if( h->obe_system == OBE_SYSTEM_TYPE_LOW_LATENCY ) { coded_frame->arrival_time = arrival_time; //printf("\n Encode Latency %"PRIi64" \n", obe_mdate() - coded_frame->arrival_time ); } add_to_smoothing_queue( h, coded_frame ); } } end: if( s ) x264_encoder_close( s ); free( enc_params ); return NULL; }