static void *start_encoder( void *ptr ) { obe_aud_enc_params_t *enc_params = ptr; obe_t *h = enc_params->h; obe_encoder_t *encoder = enc_params->encoder; obe_output_stream_t *stream = enc_params->stream; obe_raw_frame_t *raw_frame; obe_coded_frame_t *coded_frame; void *audio_buf = NULL; int64_t cur_pts = -1, pts_increment; int i, frame_size, ret, got_pkt, num_frames = 0, total_size = 0, audio_buf_len; AVFifoBuffer *out_fifo = NULL; AVAudioResampleContext *avr = NULL; AVPacket pkt; AVCodecContext *codec = NULL; AVFrame *frame = NULL; AVDictionary *opts = NULL; char is_latm[2]; avcodec_register_all(); codec = avcodec_alloc_context3( NULL ); if( !codec ) { fprintf( stderr, "Malloc failed\n" ); goto finish; } for( i = 0; lavc_encoders[i].obe_name != -1; i++ ) { if( lavc_encoders[i].obe_name == stream->stream_format ) break; } if( lavc_encoders[i].obe_name == -1 ) { fprintf( stderr, "[lavc] Could not find encoder1\n" ); goto finish; } AVCodec *enc = avcodec_find_encoder( lavc_encoders[i].lavc_name ); if( !enc ) { fprintf( stderr, "[lavc] Could not find encoder2\n" ); goto finish; } if( enc->sample_fmts[0] == -1 ) { fprintf( stderr, "[lavc] No valid sample formats\n" ); goto finish; } codec->sample_rate = enc_params->sample_rate; codec->bit_rate = stream->bitrate * 1000; codec->sample_fmt = enc->sample_fmts[0]; codec->channels = av_get_channel_layout_nb_channels( stream->channel_layout ); codec->channel_layout = stream->channel_layout; codec->time_base.num = 1; codec->time_base.den = OBE_CLOCK; codec->profile = stream->aac_opts.aac_profile == AAC_HE_V2 ? FF_PROFILE_AAC_HE_V2 : stream->aac_opts.aac_profile == AAC_HE_V1 ? FF_PROFILE_AAC_HE : FF_PROFILE_AAC_LOW; snprintf( is_latm, sizeof(is_latm), "%i", stream->aac_opts.latm_output ); av_dict_set( &opts, "latm", is_latm, 0 ); av_dict_set( &opts, "header_period", "2", 0 ); if( avcodec_open2( codec, enc, &opts ) < 0 ) { fprintf( stderr, "[lavc] Could not open encoder\n" ); goto finish; } avr = avresample_alloc_context(); if( !avr ) { fprintf( stderr, "Malloc failed\n" ); goto finish; } av_opt_set_int( avr, "in_channel_layout", codec->channel_layout, 0 ); av_opt_set_int( avr, "in_sample_fmt", enc_params->input_sample_format, 0 ); av_opt_set_int( avr, "in_sample_rate", enc_params->sample_rate, 0 ); av_opt_set_int( avr, "out_channel_layout", codec->channel_layout, 0 ); av_opt_set_int( avr, "out_sample_fmt", codec->sample_fmt, 0 ); av_opt_set_int( avr, "dither_method", AV_RESAMPLE_DITHER_TRIANGULAR_NS, 0 ); if( avresample_open( avr ) < 0 ) { fprintf( stderr, "Could not open AVResample\n" ); goto finish; } /* The number of samples per E-AC3 frame is unknown until the encoder is ready */ if( stream->stream_format == AUDIO_E_AC_3 || stream->stream_format == AUDIO_AAC ) { pthread_mutex_lock( &encoder->queue.mutex ); encoder->is_ready = 1; encoder->num_samples = codec->frame_size; /* Broadcast because input and muxer can be stuck waiting for encoder */ pthread_cond_broadcast( &encoder->queue.in_cv ); pthread_mutex_unlock( &encoder->queue.mutex ); } frame_size = (double)codec->frame_size * 125 * stream->bitrate * enc_params->frames_per_pes / enc_params->sample_rate; /* NB: libfdk-aac already doubles the frame size appropriately */ pts_increment = (double)codec->frame_size * OBE_CLOCK * enc_params->frames_per_pes / enc_params->sample_rate; out_fifo = av_fifo_alloc( frame_size ); if( !out_fifo ) { fprintf( stderr, "Malloc failed\n" ); goto finish; } audio_buf_len = codec->frame_size * av_get_bytes_per_sample( codec->sample_fmt ) * codec->channels; audio_buf = av_malloc( audio_buf_len ); if( !audio_buf ) { fprintf( stderr, "Malloc failed\n" ); goto finish; } frame = avcodec_alloc_frame(); if( !frame ) { fprintf( stderr, "Could not allocate frame\n" ); goto finish; } while( 1 ) { /* TODO: detect bitrate or channel reconfig */ pthread_mutex_lock( &encoder->queue.mutex ); while( !encoder->queue.size && !encoder->cancel_thread ) pthread_cond_wait( &encoder->queue.in_cv, &encoder->queue.mutex ); if( encoder->cancel_thread ) { pthread_mutex_unlock( &encoder->queue.mutex ); goto finish; } raw_frame = encoder->queue.queue[0]; pthread_mutex_unlock( &encoder->queue.mutex ); if( cur_pts == -1 ) cur_pts = raw_frame->pts; if( avresample_convert( avr, NULL, 0, raw_frame->audio_frame.num_samples, raw_frame->audio_frame.audio_data, raw_frame->audio_frame.linesize, raw_frame->audio_frame.num_samples ) < 0 ) { syslog( LOG_ERR, "[lavc] Sample format conversion failed\n" ); break; } raw_frame->release_data( raw_frame ); raw_frame->release_frame( raw_frame ); remove_from_queue( &encoder->queue ); while( avresample_available( avr ) >= codec->frame_size ) { got_pkt = 0; avcodec_get_frame_defaults( frame ); frame->nb_samples = codec->frame_size; avresample_read( avr, &audio_buf, codec->frame_size ); if( avcodec_fill_audio_frame( frame, codec->channels, codec->sample_fmt, audio_buf, audio_buf_len, 0 ) < 0 ) { syslog( LOG_ERR, "[lavc] Could not fill audio frame\n" ); break; } av_init_packet( &pkt ); pkt.data = NULL; pkt.size = 0; ret = avcodec_encode_audio2( codec, &pkt, frame, &got_pkt ); if( ret < 0 ) { syslog( LOG_ERR, "[lavc] Audio encoding failed\n" ); goto finish; } if( !got_pkt ) continue; total_size += pkt.size; num_frames++; if( av_fifo_realloc2( out_fifo, av_fifo_size( out_fifo ) + pkt.size ) < 0 ) { syslog( LOG_ERR, "Malloc failed\n" ); break; } av_fifo_generic_write( out_fifo, pkt.data, pkt.size, NULL ); obe_free_packet( &pkt ); if( num_frames == enc_params->frames_per_pes ) { coded_frame = new_coded_frame( encoder->output_stream_id, total_size ); if( !coded_frame ) { syslog( LOG_ERR, "Malloc failed\n" ); goto finish; } av_fifo_generic_read( out_fifo, coded_frame->data, total_size, NULL ); coded_frame->pts = cur_pts; coded_frame->random_access = 1; /* Every frame output is a random access point */ add_to_queue( &h->mux_queue, coded_frame ); /* We need to generate PTS because frame sizes have changed */ cur_pts += pts_increment; total_size = num_frames = 0; } } } finish: if( frame ) avcodec_free_frame( &frame ); if( audio_buf ) av_free( audio_buf ); if( out_fifo ) av_fifo_free( out_fifo ); if( avr ) avresample_free( &avr ); if( codec ) { avcodec_close( codec ); av_free( codec ); } free( enc_params ); 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; }