blob: ef9a4627a1aff30f52ff96e98b13466fb05c08f5 [file] [log] [blame]
Song Zhao6859d412020-06-15 17:16:04 -07001/*
2 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
3 *
4 * This source code is subject to the terms and conditions defined in the
5 * file 'LICENSE' which is part of this source code package.
6 *
7 * Description:
8 */
9
10#include <pthread.h>
11#include <stdbool.h>
12#include <stdlib.h>
13#include <stdio.h>
14
15#include "aml_avsync.h"
16#include "queue.h"
17#include "pattern.h"
18#include "tsync.h"
19#include "aml_avsync_log.h"
20
Song Zhao6859d412020-06-15 17:16:04 -070021enum sync_state {
22 AV_SYNC_STAT_INIT = 0,
23 AV_SYNC_STAT_RUNNING = 1,
24 AV_SYNC_STAT_SYNC_SETUP = 2,
25 AV_SYNC_STAT_SYNC_LOST = 3,
26};
27
28struct av_sync_session {
29 /* session id attached */
30 int session_id;
31 /* playback time, will stop increasing during pause */
32 pts90K stream_time;
33 pts90K vpts;
34
35 /* phase adjustment of stream time for rate control */
36 pts90K phase;
37 bool phase_set;
38
39 /* pts of last rendered frame */
40 pts90K last_pts;
41 struct vframe *last_frame;
42
43 /* monotonic system time, keep increasing during pause */
44 struct timespec system_time;
45 bool first_frame_toggled;
46 /* Whether in pause state */
47 bool paused;
48 enum sync_mode mode;
49 enum sync_state state;
50 void *pattern_detector;
51 void *frame_q;
52 /* start threshold */
53 int start_thres;
54
55 /* display property */
56 int delay;
57 pts90K vsync_interval;
58
59 /* state lock */
60 pthread_mutex_t lock;
61 /* pattern */
62 int last_holding_peroid;
63 bool tsync_started;
64};
65
66#define MAX_FRAME_NUM 32
67#define DEFAULT_START_THRESHOLD 2
68#define TIME_UNIT90K (90000)
69#define AV_DISCONTINUE_THREDHOLD_MIN (TIME_UNIT90K * 3)
70
71static bool frame_expire(struct av_sync_session* avsync,
72 uint32_t systime,
73 struct vframe * frame,
74 struct vframe * next_frame,
75 int toggle_cnt);
76static void pattern_detect(struct av_sync_session* avsync,
77 int cur_period,
78 int last_period);
79
Song Zhaobf35d4a2020-06-18 22:36:29 -070080void* av_sync_create(int session_id,
81 enum sync_mode mode,
82 int start_thres,
Song Zhao6859d412020-06-15 17:16:04 -070083 int delay, pts90K vsync_interval)
84{
85 struct av_sync_session *avsync = NULL;
86
87 if (start_thres > 5) {
88 log_error("start_thres too big: %d", start_thres);
89 return NULL;
90 }
91 if (delay != 1 && delay != 2) {
92 log_error("invalid delay: %d\n", delay);
93 return NULL;
94 }
Song Zhaoebb53bb2020-07-10 17:57:34 -070095 if (vsync_interval < 750 || vsync_interval > 3750) {
Song Zhao6859d412020-06-15 17:16:04 -070096 log_error("invalid vsync interval: %d", vsync_interval);
97 return NULL;
98 }
99
100 avsync = (struct av_sync_session *)calloc(1, sizeof(*avsync));
101 if (!avsync) {
102 log_error("OOM");
103 return NULL;
104 }
105 avsync->pattern_detector = create_pattern_detector();
106 if (!avsync->pattern_detector) {
107 log_error("pd create fail");
108 free(avsync);
109 return NULL;
110 }
111 avsync->state = AV_SYNC_STAT_INIT;
112 avsync->first_frame_toggled = false;
113 avsync->paused = false;
114 avsync->phase_set = false;
115 avsync->session_id = session_id;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700116 avsync->mode = mode;
Song Zhao6859d412020-06-15 17:16:04 -0700117 avsync->last_frame = NULL;
118 avsync->tsync_started = false;
119 if (!start_thres)
120 avsync->start_thres = DEFAULT_START_THRESHOLD;
121 else
122 avsync->start_thres = start_thres;
123 avsync->delay = delay;
124 avsync->vsync_interval = vsync_interval;
125
126 avsync->frame_q = create_q(MAX_FRAME_NUM);
127 if (!avsync->frame_q) {
128 log_error("create queue fail");
129 destroy_pattern_detector(avsync->pattern_detector);
130 free(avsync);
131 return NULL;
132 }
133 //TODO: connect kernel session
134
Song Zhaoebb53bb2020-07-10 17:57:34 -0700135 /* just in case sysnode is wrongly set */
136 tsync_send_video_pause(avsync->session_id, false);
137
Song Zhao6859d412020-06-15 17:16:04 -0700138 pthread_mutex_init(&avsync->lock, NULL);
Song Zhaobf35d4a2020-06-18 22:36:29 -0700139 log_info("mode: %d start_thres: %d delay: %d interval: %d done\n",
140 mode, start_thres, delay, vsync_interval);
Song Zhao6859d412020-06-15 17:16:04 -0700141 return avsync;
142}
143
144static int internal_stop(struct av_sync_session *avsync)
145{
146 int ret = 0;
147 struct vframe *frame;
148
149 pthread_mutex_lock(&avsync->lock);
150 if (avsync->state == AV_SYNC_STAT_INIT)
151 goto exit;
152
153 while (!dqueue_item(avsync->frame_q, (void **)&frame)) {
154 frame->free(frame);
155 }
156
157 avsync->state = AV_SYNC_STAT_INIT;
158exit:
159 pthread_mutex_unlock(&avsync->lock);
160 return ret;
161}
162
163/* destroy and detach from kernel session */
164void av_sync_destroy(void *sync)
165{
166 struct av_sync_session *avsync = (struct av_sync_session *)sync;
167
168 if (!avsync)
169 return;
170
171 log_info("begin");
172 if (avsync->state != AV_SYNC_STAT_INIT)
173 internal_stop(avsync);
174
175 /* all frames are freed */
176 //TODO: disconnect kernel session
Song Zhaobf35d4a2020-06-18 22:36:29 -0700177 tsync_set_pts_inc_mode(avsync->session_id, false);
Song Zhao7f969822020-07-21 15:47:07 -0700178 if (avsync->mode == AV_SYNC_MODE_VMASTER)
Song Zhaode890752020-07-21 13:36:49 -0700179 tsync_enable(avsync->session_id, false);
Song Zhao6859d412020-06-15 17:16:04 -0700180 pthread_mutex_destroy(&avsync->lock);
181 destroy_q(avsync->frame_q);
182 destroy_pattern_detector(avsync->pattern_detector);
183 free(avsync);
184 log_info("done");
185}
186
187int av_sync_pause(void *sync, bool pause)
188{
189 struct av_sync_session *avsync = (struct av_sync_session *)sync;
190
191 if (!avsync)
192 return -1;
193
Song Zhaode890752020-07-21 13:36:49 -0700194 if (avsync->mode == AV_SYNC_MODE_VMASTER) {
Song Zhaoebb53bb2020-07-10 17:57:34 -0700195 tsync_send_video_pause(avsync->session_id, pause);
Song Zhaode890752020-07-21 13:36:49 -0700196 avsync->paused = pause;
197 log_info("paused:%d\n", pause);
198 } else {
199 log_info("ignore paused:%d in mode %d", avsync->mode);
200 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700201
Song Zhao6859d412020-06-15 17:16:04 -0700202 return 0;
203}
204
205int av_sync_push_frame(void *sync , struct vframe *frame)
206{
207 int ret;
208 struct av_sync_session *avsync = (struct av_sync_session *)sync;
209
210 if (!avsync)
211 return -1;
212
213 frame->hold_period = 0;
214 ret = queue_item(avsync->frame_q, frame);
215 if (avsync->state == AV_SYNC_STAT_INIT &&
216 queue_size(avsync->frame_q) >= avsync->start_thres) {
217 avsync->state = AV_SYNC_STAT_RUNNING;
218 log_info("state: init --> running");
219 }
220
221 if (ret)
222 log_error("%s queue fail:%d", ret);
223 return ret;
224
225}
226
227struct vframe *av_sync_pop_frame(void *sync)
228{
229 struct vframe *frame = NULL;
230 struct av_sync_session *avsync = (struct av_sync_session *)sync;
231 int toggle_cnt = 0;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700232 uint32_t systime;
Song Zhao6859d412020-06-15 17:16:04 -0700233
234 pthread_mutex_lock(&avsync->lock);
Song Zhaode890752020-07-21 13:36:49 -0700235 if (avsync->state == AV_SYNC_STAT_INIT) {
236 log_trace("in state INIT");
Song Zhao6859d412020-06-15 17:16:04 -0700237 goto exit;
Song Zhaode890752020-07-21 13:36:49 -0700238 }
Song Zhao6859d412020-06-15 17:16:04 -0700239
240 if (!avsync->tsync_started) {
241 if (peek_item(avsync->frame_q, (void **)&frame, 0) || !frame) {
242 log_info("empty q");
243 goto exit;
244 }
245
Song Zhaobf35d4a2020-06-18 22:36:29 -0700246 if (tsync_enable(avsync->session_id, true))
247 log_error("enable tsync fail");
248 if (avsync->mode == AV_SYNC_MODE_VMASTER) {
249 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_VMASTER))
250 log_error("set vmaster mode fail");
251 if (tsync_set_pcr(avsync->session_id, frame->pts))
252 log_error("set pcr fail");
253 log_info("update pcr to: %u", frame->pts);
254 if (tsync_set_pts_inc_mode(avsync->session_id, true))
255 log_error("set inc mode fail");
Song Zhaobf35d4a2020-06-18 22:36:29 -0700256 } else if (avsync->mode == AV_SYNC_MODE_AMASTER) {
Song Zhaoebb53bb2020-07-10 17:57:34 -0700257 if (tsync_set_pts_inc_mode(avsync->session_id, false))
258 log_error("set inc mode fail");
Song Zhaobf35d4a2020-06-18 22:36:29 -0700259 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_AMASTER))
260 log_error("set amaster mode fail");
261 } else {
262 //PCR master mode should be set alreay, but it won't hurt to set again.
263 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_PCR_MASTER))
264 log_error("set pcrmaster mode fail");
265 }
266
Song Zhao58d3a1c2020-08-04 16:51:30 -0700267 tsync_set_video_peek_mode(avsync->session_id);
268 tsync_disable_video_stop_event(avsync->session_id, true);
Song Zhao0adb3b52020-07-27 16:15:04 -0700269 /* video start ASAP */
270 tsync_set_video_sync_thres(avsync->session_id, false);
Song Zhao6859d412020-06-15 17:16:04 -0700271 /* video start event */
Song Zhaobf35d4a2020-06-18 22:36:29 -0700272 if (tsync_send_video_start(avsync->session_id, frame->pts))
273 log_error("send video start fail");
274 else
275 log_info("video start %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700276 avsync->tsync_started = true;
277 }
278
Song Zhaobf35d4a2020-06-18 22:36:29 -0700279 systime = tsync_get_pcr(avsync->session_id);
Song Zhao6859d412020-06-15 17:16:04 -0700280 while (!peek_item(avsync->frame_q, (void **)&frame, 0)) {
281 struct vframe *next_frame = NULL;
282
283 peek_item(avsync->frame_q, (void **)&next_frame, 1);
284 if (next_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700285 log_debug("cur_f %u next_f %u", frame->pts, next_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700286 if (frame_expire(avsync, systime, frame, next_frame, toggle_cnt)) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700287 log_debug("cur_f %u expire", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700288 toggle_cnt++;
289
290 pattern_detect(avsync,
291 (avsync->last_frame?avsync->last_frame->hold_period:0),
292 avsync->last_holding_peroid);
293 if (avsync->last_frame)
294 avsync->last_holding_peroid = avsync->last_frame->hold_period;
295
296 dqueue_item(avsync->frame_q, (void **)&frame);
297 if (avsync->last_frame) {
298 /* free frame that are not for display */
299 if (toggle_cnt > 1)
300 avsync->last_frame->free(avsync->last_frame);
301 } else {
302 avsync->first_frame_toggled = true;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700303 log_info("first frame %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700304 }
305 avsync->last_frame = frame;
306 } else
307 break;
308 }
309
310exit:
311 pthread_mutex_unlock(&avsync->lock);
312 if (avsync->last_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700313 log_debug("pop %u", avsync->last_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700314 else
315 log_debug("pop (nil)");
316 if (avsync->last_frame)
317 avsync->last_frame->hold_period++;
318 return avsync->last_frame;
319}
320
321void av_sync_update_vsync_interval(void *sync, pts90K vsync_interval)
322{
323 struct av_sync_session *avsync = (struct av_sync_session *)sync;
324
325 pthread_mutex_lock(&avsync->lock);
326 avsync->vsync_interval = vsync_interval;
327 if (avsync->state >= AV_SYNC_STAT_RUNNING) {
328 reset_pattern(avsync->pattern_detector);
329 avsync->phase_set = false;
330 }
331 pthread_mutex_unlock(&avsync->lock);
332}
333
334static inline uint32_t abs_diff(uint32_t a, uint32_t b)
335{
336 return a > b ? a - b : b - a;
337}
338
339static bool frame_expire(struct av_sync_session* avsync,
340 uint32_t systime,
341 struct vframe * frame,
342 struct vframe * next_frame,
343 int toggle_cnt)
344{
345 uint32_t fpts = frame->pts;
346 bool expire = false;
347 uint32_t pts_correction = avsync->delay * avsync->vsync_interval;
348
349 if (!fpts) {
350 if (avsync->last_frame) {
351 /* try to accumulate duration as PTS */
352 fpts = avsync->vpts + avsync->last_frame->duration;
353 } else {
354 fpts = avsync->vpts;
355 }
356 }
357 systime += pts_correction;
358
359 /* phase adjustment */
360 if (avsync->phase_set)
361 systime += avsync->phase;
362
Song Zhaobf35d4a2020-06-18 22:36:29 -0700363 log_trace("systime:%u phase:%u correct:%u", systime,
Song Zhao6859d412020-06-15 17:16:04 -0700364 avsync->phase_set?avsync->phase:0, pts_correction);
365 if (abs_diff(systime, fpts) > AV_DISCONTINUE_THREDHOLD_MIN &&
366 avsync->first_frame_toggled) {
367 /* ignore discontinity under pause */
368 if (avsync->paused && avsync->mode != AV_SYNC_MODE_PCR_MASTER)
369 return false;
370
371 log_warn("sync lost systime:%x fpts:%x", systime, fpts);
372 avsync->state = AV_SYNC_STAT_SYNC_LOST;
373 avsync->phase_set = false;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700374 if ((int)(systime - fpts) > 0) {
Song Zhaod3b2b852020-08-10 23:46:43 -0700375 if (frame->pts && avsync->mode == AV_SYNC_MODE_VMASTER)
Song Zhao6859d412020-06-15 17:16:04 -0700376 tsync_send_video_disc(avsync->session_id, frame->pts);
Song Zhaod3b2b852020-08-10 23:46:43 -0700377 /*catch up PCR */
378 return true;
Song Zhao6859d412020-06-15 17:16:04 -0700379 } else if (avsync->mode == AV_SYNC_MODE_PCR_MASTER) {
380 if (frame->pts)
381 tsync_send_video_disc(avsync->session_id, frame->pts);
382 else {
383 tsync_send_video_disc(avsync->session_id, fpts);
384 return true;
385 }
386 }
Song Zhao6859d412020-06-15 17:16:04 -0700387 }
388
Song Zhaoebb53bb2020-07-10 17:57:34 -0700389 expire = (int)(systime - fpts) >= 0;
Song Zhao6859d412020-06-15 17:16:04 -0700390
391 /* scatter the frame in different vsync whenever possible */
392 if (expire && next_frame && next_frame->pts && toggle_cnt) {
393 /* multi frame expired in current vsync but no frame in next vsync */
394 if (systime + avsync->vsync_interval < next_frame->pts) {
395 expire = false;
396 frame->hold_period++;
397 log_debug("unset expire systime:%d inter:%d next_pts:%d toggle_cnt:%d",
398 systime, avsync->vsync_interval, next_frame->pts, toggle_cnt);
399 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700400 } else if (!expire && next_frame && next_frame->pts && !toggle_cnt
401 && avsync->first_frame_toggled) {
Song Zhao6859d412020-06-15 17:16:04 -0700402 /* next vsync will have at least 2 frame expired */
403 if (systime + avsync->vsync_interval > next_frame->pts) {
404 expire = true;
405 log_debug("set expire systime:%d inter:%d next_pts:%d",
406 systime, avsync->vsync_interval, next_frame->pts);
407 }
408 }
409
410 correct_pattern(avsync->pattern_detector, frame, next_frame,
411 (avsync->last_frame?avsync->last_frame->hold_period:0),
412 avsync->last_holding_peroid, systime,
413 avsync->vsync_interval, &expire);
414
415 if (expire) {
416 avsync->vpts = fpts;
417 /* phase adjustment */
418 if (!avsync->phase_set) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700419 uint32_t phase_thres = avsync->vsync_interval / 4;
Song Zhao6859d412020-06-15 17:16:04 -0700420 //systime = tsync_get_pcr(avsync->session_id);
Song Zhaobf35d4a2020-06-18 22:36:29 -0700421 if ( systime > fpts && (systime - fpts) < phase_thres) {
422 /* too aligned to current VSYNC, separate them to 1/4 VSYNC */
423 avsync->phase += phase_thres - (systime - fpts);
424 avsync->phase_set = true;
425 log_info("adjust phase to %d", avsync->phase);
426 }
427 if (!avsync->phase_set && systime > fpts &&
428 systime < (fpts + avsync->vsync_interval) &&
429 (systime - fpts) > avsync->vsync_interval - phase_thres) {
430 /* too aligned to previous VSYNC, separate them to 1/4 VSYNC */
431 avsync->phase += phase_thres + fpts + avsync->vsync_interval - systime;
Song Zhao6859d412020-06-15 17:16:04 -0700432 avsync->phase_set = true;
433 log_info("adjust phase to %d", avsync->phase);
434 }
435 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700436
437 if (avsync->state != AV_SYNC_STAT_SYNC_SETUP)
438 log_info("sync setup");
439 avsync->state = AV_SYNC_STAT_SYNC_SETUP;
Song Zhao6859d412020-06-15 17:16:04 -0700440 }
441
442 return expire;
443}
444
445static void pattern_detect(struct av_sync_session* avsync, int cur_period, int last_period)
446{
447 log_trace("cur_period: %d last_period: %d", cur_period, last_period);
448 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P32, cur_period, last_period);
449 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P22, cur_period, last_period);
450 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P41, cur_period, last_period);
Song Zhao6859d412020-06-15 17:16:04 -0700451}