From 56c502b2544435a6d237168c9fb5869b1a3b2a41 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sun, 3 Jul 2022 14:43:38 +0900 Subject: [PATCH] obs-qsv11: Fix timestamp for fractional frame rate This commit rounds the timestamp to the integer multiple of fps_den. Prior to this change, since the timestamp in MFX is defined in units of 90 kHz, timestamps for 60000/1001 fps is not accurately expressed. Also the encoder sometimes returns the decode time stamp slightly off. Hence, the timestamp in OBS becomes not a multiple of fps_den after converting back from the timestamp in MFX. --- plugins/obs-qsv11/obs-qsv11.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/plugins/obs-qsv11/obs-qsv11.c b/plugins/obs-qsv11/obs-qsv11.c index 34a88a0c4..2b31c6529 100644 --- a/plugins/obs-qsv11/obs-qsv11.c +++ b/plugins/obs-qsv11/obs-qsv11.c @@ -826,8 +826,23 @@ static void obs_qsv_video_info(void *data, struct video_scale_info *info) cap_resolution(obsqsv->encoder, info); } +static mfxU64 ts_obs_to_mfx(int64_t ts, const struct video_output_info *voi) +{ + return ts * 90000 / voi->fps_num; +} + +static int64_t ts_mfx_to_obs(mfxI64 ts, const struct video_output_info *voi) +{ + int64_t div = 90000 * (int64_t)voi->fps_den; + /* Round to the nearest integer multiple of `voi->fps_den`. */ + if (ts < 0) + return (ts * voi->fps_num - div / 2) / div * voi->fps_den; + else + return (ts * voi->fps_num + div / 2) / div * voi->fps_den; +} + static void parse_packet(struct obs_qsv *obsqsv, struct encoder_packet *packet, - mfxBitstream *pBS, uint32_t fps_num, + mfxBitstream *pBS, const struct video_output_info *voi, bool *received_packet) { uint8_t *start, *end; @@ -845,7 +860,7 @@ static void parse_packet(struct obs_qsv *obsqsv, struct encoder_packet *packet, packet->data = obsqsv->packet_data.array; packet->size = obsqsv->packet_data.num; packet->type = OBS_ENCODER_VIDEO; - packet->pts = pBS->TimeStamp * fps_num / 90000; + packet->pts = ts_mfx_to_obs((mfxI64)pBS->TimeStamp, voi); packet->keyframe = (pBS->FrameType & MFX_FRAMETYPE_IDR); uint16_t frameType = pBS->FrameType; @@ -906,7 +921,7 @@ static void parse_packet(struct obs_qsv *obsqsv, struct encoder_packet *packet, g_prevDts = packet->dts; } } else { - packet->dts = pBS->DecodeTimeStamp * fps_num / 90000; + packet->dts = ts_mfx_to_obs(pBS->DecodeTimeStamp, voi); } #if 0 @@ -940,7 +955,7 @@ static bool obs_qsv_encode(void *data, struct encoder_frame *frame, int ret; - mfxU64 qsvPTS = frame->pts * 90000 / voi->fps_num; + mfxU64 qsvPTS = ts_obs_to_mfx(frame->pts, voi); // FIXME: remove null check from the top of this function // if we actually do expect null frames to complete output. @@ -959,7 +974,7 @@ static bool obs_qsv_encode(void *data, struct encoder_frame *frame, return false; } - parse_packet(obsqsv, packet, pBS, voi->fps_num, received_packet); + parse_packet(obsqsv, packet, pBS, voi, received_packet); LeaveCriticalSection(&g_QsvCs); @@ -991,7 +1006,7 @@ static bool obs_qsv_encode_tex(void *data, uint32_t handle, int64_t pts, int ret; - mfxU64 qsvPTS = pts * 90000 / voi->fps_num; + mfxU64 qsvPTS = ts_obs_to_mfx(pts, voi); ret = qsv_encoder_encode_tex(obsqsv->context, qsvPTS, handle, lock_key, next_key, &pBS); @@ -1002,7 +1017,7 @@ static bool obs_qsv_encode_tex(void *data, uint32_t handle, int64_t pts, return false; } - parse_packet(obsqsv, packet, pBS, voi->fps_num, received_packet); + parse_packet(obsqsv, packet, pBS, voi, received_packet); LeaveCriticalSection(&g_QsvCs);