mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-07-14 23:34:08 +00:00
Improve async source render timing
This helps ensure that an asynchronous video source is played as close to its framerate as possible, reduces the risk of duplication as much as possible, and helps to ensure that playback is as smooth as possible.
This commit is contained in:
parent
52e08249f1
commit
11b0fe122c
|
@ -1501,9 +1501,14 @@ void obs_source_output_audio(obs_source_t source,
|
|||
|
||||
static inline bool frame_out_of_bounds(obs_source_t source, uint64_t ts)
|
||||
{
|
||||
return ((ts - source->last_frame_ts) > MAX_TIMESTAMP_JUMP);
|
||||
if (ts < source->last_frame_ts)
|
||||
return ((source->last_frame_ts - ts) > MAX_TIMESTAMP_JUMP);
|
||||
else
|
||||
return ((ts - source->last_frame_ts) > MAX_TIMESTAMP_JUMP);
|
||||
}
|
||||
|
||||
/* #define DEBUG_ASYNC_FRAMES 1 */
|
||||
|
||||
static bool ready_async_frame(obs_source_t source, uint64_t sys_time)
|
||||
{
|
||||
struct obs_source_frame *next_frame = source->video_frames.array[0];
|
||||
|
@ -1512,26 +1517,60 @@ static bool ready_async_frame(obs_source_t source, uint64_t sys_time)
|
|||
uint64_t frame_time = next_frame->timestamp;
|
||||
uint64_t frame_offset = 0;
|
||||
|
||||
#if DEBUG_ASYNC_FRAMES
|
||||
blog(LOG_DEBUG, "source->last_frame_ts: %llu, frame_time: %llu, "
|
||||
"sys_offset: %llu, frame_offset: %llu, "
|
||||
"number of frames: %lu",
|
||||
source->last_frame_ts, frame_time, sys_offset,
|
||||
frame_time - source->last_frame_ts,
|
||||
(unsigned long)source->video_frames.num);
|
||||
#endif
|
||||
|
||||
/* account for timestamp invalidation */
|
||||
if (frame_out_of_bounds(source, frame_time)) {
|
||||
#if DEBUG_ASYNC_FRAMES
|
||||
blog(LOG_DEBUG, "timing jump");
|
||||
#endif
|
||||
source->last_frame_ts = next_frame->timestamp;
|
||||
return true;
|
||||
} else {
|
||||
frame_offset = frame_time - source->last_frame_ts;
|
||||
source->last_frame_ts += frame_offset;
|
||||
source->last_frame_ts += sys_offset;
|
||||
}
|
||||
|
||||
while (frame_offset <= sys_offset) {
|
||||
while (source->last_frame_ts > next_frame->timestamp) {
|
||||
|
||||
/* this tries to reduce the needless frame duplication, also
|
||||
* helps smooth out async rendering to frame boundaries. In
|
||||
* other words, tries to keep the framerate as smooth as
|
||||
* possible */
|
||||
if ((source->last_frame_ts - next_frame->timestamp) < 1000000)
|
||||
break;
|
||||
|
||||
if (frame)
|
||||
da_erase(source->video_frames, 0);
|
||||
|
||||
#if DEBUG_ASYNC_FRAMES
|
||||
blog(LOG_DEBUG, "new frame, "
|
||||
"source->last_frame_ts: %llu, "
|
||||
"next_frame->timestamp: %llu",
|
||||
source->last_frame_ts,
|
||||
next_frame->timestamp);
|
||||
#endif
|
||||
|
||||
obs_source_frame_destroy(frame);
|
||||
|
||||
if (source->video_frames.num == 1)
|
||||
return true;
|
||||
|
||||
frame = next_frame;
|
||||
da_erase(source->video_frames, 0);
|
||||
next_frame = source->video_frames.array[0];
|
||||
next_frame = source->video_frames.array[1];
|
||||
|
||||
/* more timestamp checking and compensating */
|
||||
if ((next_frame->timestamp - frame_time) > MAX_TIMESTAMP_JUMP) {
|
||||
#if DEBUG_ASYNC_FRAMES
|
||||
blog(LOG_DEBUG, "timing jump");
|
||||
#endif
|
||||
source->last_frame_ts =
|
||||
next_frame->timestamp - frame_offset;
|
||||
}
|
||||
|
@ -1540,7 +1579,10 @@ static bool ready_async_frame(obs_source_t source, uint64_t sys_time)
|
|||
frame_offset = frame_time - source->last_frame_ts;
|
||||
}
|
||||
|
||||
obs_source_frame_destroy(frame);
|
||||
#if DEBUG_ASYNC_FRAMES
|
||||
if (!frame)
|
||||
blog(LOG_DEBUG, "no frame!");
|
||||
#endif
|
||||
|
||||
return frame != NULL;
|
||||
}
|
||||
|
@ -1573,11 +1615,11 @@ struct obs_source_frame *obs_source_get_frame(obs_source_t source)
|
|||
|
||||
pthread_mutex_lock(&source->video_mutex);
|
||||
|
||||
sys_time = os_gettime_ns();
|
||||
|
||||
if (!source->video_frames.num)
|
||||
goto unlock;
|
||||
|
||||
sys_time = os_gettime_ns();
|
||||
|
||||
if (!source->last_frame_ts) {
|
||||
frame = source->video_frames.array[0];
|
||||
da_erase(source->video_frames, 0);
|
||||
|
@ -1589,13 +1631,16 @@ struct obs_source_frame *obs_source_get_frame(obs_source_t source)
|
|||
|
||||
/* reset timing to current system time */
|
||||
if (frame) {
|
||||
uint64_t min_expected_sys_ts =
|
||||
frame->timestamp + source->timing_adjust;
|
||||
|
||||
source->timing_adjust = sys_time - frame->timestamp;
|
||||
source->timing_set = true;
|
||||
}
|
||||
|
||||
unlock:
|
||||
source->last_sys_timestamp = sys_time;
|
||||
|
||||
unlock:
|
||||
pthread_mutex_unlock(&source->video_mutex);
|
||||
|
||||
if (frame)
|
||||
|
|
Loading…
Reference in a new issue