Exemplo n.º 1
0
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, &param );

    /* 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;
}
Exemplo n.º 2
0
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, &param );

    /* 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;
}
Exemplo n.º 3
0
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;
}