1
votes

I have yuv frames that I am converting to h264 frames using libx264 successfully. I know the time difference between each frame. Then I use the following ffmpeg command to convert h264 video file to mp4:

ffmpeg.exe -i frames.h264 -c:v copy -f mp4 frames.mp4

This works. However I can't figure out how to set the variable frame rate properly. The movie plays too fast. I don't have fixed frame rate, otherwise I would set that and the resulting mp4 is the correct duration.

I have the time difference between each frame in milliseconds. How can I set the timebase and pts so that when I convert the h264 file to mp4, it is the correct duration.

bool X264Encoder::init(int width, int height, 
AVRational &timebase,  // what should timebase be?
int fps)
{
   x264_param_t param;
    if( x264_param_default_preset( &param, "medium", NULL ) < 0 )
    { return false; }

    /* Configure non-default params */
    param.i_bitdepth = 8;
    param.i_csp = X264_CSP_I420;
    param.i_width  = width;
    param.i_height = height;
    param.b_vfr_input = 0;
    param.b_repeat_headers = 1;
    param.b_annexb = 1;
    if(0) // don't have fixed frame rate, can't use it
    {
        param.b_vfr_input = 0;
        param.i_fps_num = 4;
        param.i_fps_den = 1;
    }
    else
    {
        param.b_vfr_input = 1;
        param.i_timebase_num = timebase.den;
        param.i_timebase_den = timebase.num;
    }

    if( x264_param_apply_profile(&param, "high" ) < 0 )
    { return false; }

    m_pic_in = new x264_picture_t;
    x264_picture_init(m_pic_in);
    m_pic_in->img.i_csp = X264_CSP_I420;
    m_pic_in->img.i_plane = 3;

    m_encoder = x264_encoder_open(&param);

}

//This is called for each frame
void X264Encoder::encode(uint8_t *plane[4], int64_t pts) // what should pts be?
{
    m_pic_in->img.plane[0] = plane[0];  // Y frame
    m_pic_in->img.i_stride[0] = m_width;
    m_pic_in->img.plane[1] = plane[1];  // U frame
    m_pic_in->img.i_stride[1] = m_width;
    m_pic_in->img.plane[2] = plane[2];  // V frame
    m_pic_in->img.i_stride[2] = m_width;

    x264_picture_t pic_out;
    m_pic_in->i_pts = pts;
    int frame_size = x264_encoder_encode(m_encoder, &m_nals, &m_i_nals, m_pic_in, &pic_out);

    if(frame_size <= 0)
    {
        return;
    }

    writeH264Frame(m_nals->p_payload, frame_size); // basically same as fwrite
}
1

1 Answers

2
votes

In general timebase is units of your frame timestamps i.e. if your first frames have deltas 40ms and 33ms than you set timebase 1/1000 and provide pts = 0, 40, 73. But raw H.264 (Annex B) format doesn't have timestamp information so it is not enough to save VFR video. You need to provide timestamp file when converting to mp4 or you need save directly to mp4 while you have pts/dts information of encoded frames.