blob: eed498753b1a106a81a7e6b642cf0b79fbf02b17 [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 Zhaode890752020-07-21 13:36:49 -0700178 if (sync->mode == AV_SYNC_MODE_VMASTER)
179 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");
256 if (tsync_set_video_peek_mode(avsync->session_id))
257 log_error("set peek mode fail");
258 } else if (avsync->mode == AV_SYNC_MODE_AMASTER) {
Song Zhaoebb53bb2020-07-10 17:57:34 -0700259 if (tsync_set_pts_inc_mode(avsync->session_id, false))
260 log_error("set inc mode fail");
Song Zhaobf35d4a2020-06-18 22:36:29 -0700261 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_AMASTER))
262 log_error("set amaster mode fail");
263 } else {
264 //PCR master mode should be set alreay, but it won't hurt to set again.
265 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_PCR_MASTER))
266 log_error("set pcrmaster mode fail");
267 }
268
Song Zhao6859d412020-06-15 17:16:04 -0700269 /* video start event */
Song Zhaobf35d4a2020-06-18 22:36:29 -0700270 if (tsync_send_video_start(avsync->session_id, frame->pts))
271 log_error("send video start fail");
272 else
273 log_info("video start %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700274 avsync->tsync_started = true;
275 }
276
Song Zhaobf35d4a2020-06-18 22:36:29 -0700277 systime = tsync_get_pcr(avsync->session_id);
Song Zhao6859d412020-06-15 17:16:04 -0700278 while (!peek_item(avsync->frame_q, (void **)&frame, 0)) {
279 struct vframe *next_frame = NULL;
280
281 peek_item(avsync->frame_q, (void **)&next_frame, 1);
282 if (next_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700283 log_debug("cur_f %u next_f %u", frame->pts, next_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700284 if (frame_expire(avsync, systime, frame, next_frame, toggle_cnt)) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700285 log_debug("cur_f %u expire", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700286 toggle_cnt++;
287
288 pattern_detect(avsync,
289 (avsync->last_frame?avsync->last_frame->hold_period:0),
290 avsync->last_holding_peroid);
291 if (avsync->last_frame)
292 avsync->last_holding_peroid = avsync->last_frame->hold_period;
293
294 dqueue_item(avsync->frame_q, (void **)&frame);
295 if (avsync->last_frame) {
296 /* free frame that are not for display */
297 if (toggle_cnt > 1)
298 avsync->last_frame->free(avsync->last_frame);
299 } else {
300 avsync->first_frame_toggled = true;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700301 log_info("first frame %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700302 }
303 avsync->last_frame = frame;
304 } else
305 break;
306 }
307
308exit:
309 pthread_mutex_unlock(&avsync->lock);
310 if (avsync->last_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700311 log_debug("pop %u", avsync->last_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700312 else
313 log_debug("pop (nil)");
314 if (avsync->last_frame)
315 avsync->last_frame->hold_period++;
316 return avsync->last_frame;
317}
318
319void av_sync_update_vsync_interval(void *sync, pts90K vsync_interval)
320{
321 struct av_sync_session *avsync = (struct av_sync_session *)sync;
322
323 pthread_mutex_lock(&avsync->lock);
324 avsync->vsync_interval = vsync_interval;
325 if (avsync->state >= AV_SYNC_STAT_RUNNING) {
326 reset_pattern(avsync->pattern_detector);
327 avsync->phase_set = false;
328 }
329 pthread_mutex_unlock(&avsync->lock);
330}
331
332static inline uint32_t abs_diff(uint32_t a, uint32_t b)
333{
334 return a > b ? a - b : b - a;
335}
336
337static bool frame_expire(struct av_sync_session* avsync,
338 uint32_t systime,
339 struct vframe * frame,
340 struct vframe * next_frame,
341 int toggle_cnt)
342{
343 uint32_t fpts = frame->pts;
344 bool expire = false;
345 uint32_t pts_correction = avsync->delay * avsync->vsync_interval;
346
347 if (!fpts) {
348 if (avsync->last_frame) {
349 /* try to accumulate duration as PTS */
350 fpts = avsync->vpts + avsync->last_frame->duration;
351 } else {
352 fpts = avsync->vpts;
353 }
354 }
355 systime += pts_correction;
356
357 /* phase adjustment */
358 if (avsync->phase_set)
359 systime += avsync->phase;
360
Song Zhaobf35d4a2020-06-18 22:36:29 -0700361 log_trace("systime:%u phase:%u correct:%u", systime,
Song Zhao6859d412020-06-15 17:16:04 -0700362 avsync->phase_set?avsync->phase:0, pts_correction);
363 if (abs_diff(systime, fpts) > AV_DISCONTINUE_THREDHOLD_MIN &&
364 avsync->first_frame_toggled) {
365 /* ignore discontinity under pause */
366 if (avsync->paused && avsync->mode != AV_SYNC_MODE_PCR_MASTER)
367 return false;
368
369 log_warn("sync lost systime:%x fpts:%x", systime, fpts);
370 avsync->state = AV_SYNC_STAT_SYNC_LOST;
371 avsync->phase_set = false;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700372 if ((int)(systime - fpts) > 0) {
Song Zhao6859d412020-06-15 17:16:04 -0700373 if (frame->pts)
374 tsync_send_video_disc(avsync->session_id, frame->pts);
375 else if (avsync->mode != AV_SYNC_MODE_PCR_MASTER)
376 tsync_send_video_disc(avsync->session_id, frame->pts);
377 return false;
378 } else if (avsync->mode == AV_SYNC_MODE_PCR_MASTER) {
379 if (frame->pts)
380 tsync_send_video_disc(avsync->session_id, frame->pts);
381 else {
382 tsync_send_video_disc(avsync->session_id, fpts);
383 return true;
384 }
385 }
Song Zhao6859d412020-06-15 17:16:04 -0700386 }
387
Song Zhaoebb53bb2020-07-10 17:57:34 -0700388 expire = (int)(systime - fpts) >= 0;
Song Zhao6859d412020-06-15 17:16:04 -0700389
390 /* scatter the frame in different vsync whenever possible */
391 if (expire && next_frame && next_frame->pts && toggle_cnt) {
392 /* multi frame expired in current vsync but no frame in next vsync */
393 if (systime + avsync->vsync_interval < next_frame->pts) {
394 expire = false;
395 frame->hold_period++;
396 log_debug("unset expire systime:%d inter:%d next_pts:%d toggle_cnt:%d",
397 systime, avsync->vsync_interval, next_frame->pts, toggle_cnt);
398 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700399 } else if (!expire && next_frame && next_frame->pts && !toggle_cnt
400 && avsync->first_frame_toggled) {
Song Zhao6859d412020-06-15 17:16:04 -0700401 /* next vsync will have at least 2 frame expired */
402 if (systime + avsync->vsync_interval > next_frame->pts) {
403 expire = true;
404 log_debug("set expire systime:%d inter:%d next_pts:%d",
405 systime, avsync->vsync_interval, next_frame->pts);
406 }
407 }
408
409 correct_pattern(avsync->pattern_detector, frame, next_frame,
410 (avsync->last_frame?avsync->last_frame->hold_period:0),
411 avsync->last_holding_peroid, systime,
412 avsync->vsync_interval, &expire);
413
414 if (expire) {
415 avsync->vpts = fpts;
416 /* phase adjustment */
417 if (!avsync->phase_set) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700418 uint32_t phase_thres = avsync->vsync_interval / 4;
Song Zhao6859d412020-06-15 17:16:04 -0700419 //systime = tsync_get_pcr(avsync->session_id);
Song Zhaobf35d4a2020-06-18 22:36:29 -0700420 if ( systime > fpts && (systime - fpts) < phase_thres) {
421 /* too aligned to current VSYNC, separate them to 1/4 VSYNC */
422 avsync->phase += phase_thres - (systime - fpts);
423 avsync->phase_set = true;
424 log_info("adjust phase to %d", avsync->phase);
425 }
426 if (!avsync->phase_set && systime > fpts &&
427 systime < (fpts + avsync->vsync_interval) &&
428 (systime - fpts) > avsync->vsync_interval - phase_thres) {
429 /* too aligned to previous VSYNC, separate them to 1/4 VSYNC */
430 avsync->phase += phase_thres + fpts + avsync->vsync_interval - systime;
Song Zhao6859d412020-06-15 17:16:04 -0700431 avsync->phase_set = true;
432 log_info("adjust phase to %d", avsync->phase);
433 }
434 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700435
436 if (avsync->state != AV_SYNC_STAT_SYNC_SETUP)
437 log_info("sync setup");
438 avsync->state = AV_SYNC_STAT_SYNC_SETUP;
Song Zhao6859d412020-06-15 17:16:04 -0700439 }
440
441 return expire;
442}
443
444static void pattern_detect(struct av_sync_session* avsync, int cur_period, int last_period)
445{
446 log_trace("cur_period: %d last_period: %d", cur_period, last_period);
447 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P32, cur_period, last_period);
448 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P22, cur_period, last_period);
449 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P41, cur_period, last_period);
Song Zhao6859d412020-06-15 17:16:04 -0700450}