blob: f4a25fae7299ee6aacfa7c92d74b3175205d3551 [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 Zhao6859d412020-06-15 17:16:04 -0700178 tsync_enable(avsync->session_id, false);
179 pthread_mutex_destroy(&avsync->lock);
180 destroy_q(avsync->frame_q);
181 destroy_pattern_detector(avsync->pattern_detector);
182 free(avsync);
183 log_info("done");
184}
185
186int av_sync_pause(void *sync, bool pause)
187{
188 struct av_sync_session *avsync = (struct av_sync_session *)sync;
189
190 if (!avsync)
191 return -1;
192
193 avsync->paused = pause;
Song Zhaoebb53bb2020-07-10 17:57:34 -0700194
195 if (avsync->mode == AV_SYNC_MODE_VMASTER)
196 tsync_send_video_pause(avsync->session_id, pause);
197
Song Zhao6859d412020-06-15 17:16:04 -0700198 log_info("paused:%d\n", pause);
199 return 0;
200}
201
202int av_sync_push_frame(void *sync , struct vframe *frame)
203{
204 int ret;
205 struct av_sync_session *avsync = (struct av_sync_session *)sync;
206
207 if (!avsync)
208 return -1;
209
210 frame->hold_period = 0;
211 ret = queue_item(avsync->frame_q, frame);
212 if (avsync->state == AV_SYNC_STAT_INIT &&
213 queue_size(avsync->frame_q) >= avsync->start_thres) {
214 avsync->state = AV_SYNC_STAT_RUNNING;
215 log_info("state: init --> running");
216 }
217
218 if (ret)
219 log_error("%s queue fail:%d", ret);
220 return ret;
221
222}
223
224struct vframe *av_sync_pop_frame(void *sync)
225{
226 struct vframe *frame = NULL;
227 struct av_sync_session *avsync = (struct av_sync_session *)sync;
228 int toggle_cnt = 0;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700229 uint32_t systime;
Song Zhao6859d412020-06-15 17:16:04 -0700230
231 pthread_mutex_lock(&avsync->lock);
232 if (avsync->state == AV_SYNC_STAT_INIT)
233 goto exit;
234
235 if (!avsync->tsync_started) {
236 if (peek_item(avsync->frame_q, (void **)&frame, 0) || !frame) {
237 log_info("empty q");
238 goto exit;
239 }
240
Song Zhaobf35d4a2020-06-18 22:36:29 -0700241 if (tsync_enable(avsync->session_id, true))
242 log_error("enable tsync fail");
243 if (avsync->mode == AV_SYNC_MODE_VMASTER) {
244 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_VMASTER))
245 log_error("set vmaster mode fail");
246 if (tsync_set_pcr(avsync->session_id, frame->pts))
247 log_error("set pcr fail");
248 log_info("update pcr to: %u", frame->pts);
249 if (tsync_set_pts_inc_mode(avsync->session_id, true))
250 log_error("set inc mode fail");
251 if (tsync_set_video_peek_mode(avsync->session_id))
252 log_error("set peek mode fail");
253 } else if (avsync->mode == AV_SYNC_MODE_AMASTER) {
Song Zhaoebb53bb2020-07-10 17:57:34 -0700254 if (tsync_set_pts_inc_mode(avsync->session_id, false))
255 log_error("set inc mode fail");
Song Zhaobf35d4a2020-06-18 22:36:29 -0700256 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_AMASTER))
257 log_error("set amaster mode fail");
258 } else {
259 //PCR master mode should be set alreay, but it won't hurt to set again.
260 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_PCR_MASTER))
261 log_error("set pcrmaster mode fail");
262 }
263
Song Zhao6859d412020-06-15 17:16:04 -0700264 /* video start event */
Song Zhaobf35d4a2020-06-18 22:36:29 -0700265 if (tsync_send_video_start(avsync->session_id, frame->pts))
266 log_error("send video start fail");
267 else
268 log_info("video start %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700269 avsync->tsync_started = true;
270 }
271
Song Zhaobf35d4a2020-06-18 22:36:29 -0700272 systime = tsync_get_pcr(avsync->session_id);
Song Zhao6859d412020-06-15 17:16:04 -0700273 while (!peek_item(avsync->frame_q, (void **)&frame, 0)) {
274 struct vframe *next_frame = NULL;
275
276 peek_item(avsync->frame_q, (void **)&next_frame, 1);
277 if (next_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700278 log_debug("cur_f %u next_f %u", frame->pts, next_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700279 if (frame_expire(avsync, systime, frame, next_frame, toggle_cnt)) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700280 log_debug("cur_f %u expire", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700281 toggle_cnt++;
282
283 pattern_detect(avsync,
284 (avsync->last_frame?avsync->last_frame->hold_period:0),
285 avsync->last_holding_peroid);
286 if (avsync->last_frame)
287 avsync->last_holding_peroid = avsync->last_frame->hold_period;
288
289 dqueue_item(avsync->frame_q, (void **)&frame);
290 if (avsync->last_frame) {
291 /* free frame that are not for display */
292 if (toggle_cnt > 1)
293 avsync->last_frame->free(avsync->last_frame);
294 } else {
295 avsync->first_frame_toggled = true;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700296 log_info("first frame %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700297 }
298 avsync->last_frame = frame;
299 } else
300 break;
301 }
302
303exit:
304 pthread_mutex_unlock(&avsync->lock);
305 if (avsync->last_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700306 log_debug("pop %u", avsync->last_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700307 else
308 log_debug("pop (nil)");
309 if (avsync->last_frame)
310 avsync->last_frame->hold_period++;
311 return avsync->last_frame;
312}
313
314void av_sync_update_vsync_interval(void *sync, pts90K vsync_interval)
315{
316 struct av_sync_session *avsync = (struct av_sync_session *)sync;
317
318 pthread_mutex_lock(&avsync->lock);
319 avsync->vsync_interval = vsync_interval;
320 if (avsync->state >= AV_SYNC_STAT_RUNNING) {
321 reset_pattern(avsync->pattern_detector);
322 avsync->phase_set = false;
323 }
324 pthread_mutex_unlock(&avsync->lock);
325}
326
327static inline uint32_t abs_diff(uint32_t a, uint32_t b)
328{
329 return a > b ? a - b : b - a;
330}
331
332static bool frame_expire(struct av_sync_session* avsync,
333 uint32_t systime,
334 struct vframe * frame,
335 struct vframe * next_frame,
336 int toggle_cnt)
337{
338 uint32_t fpts = frame->pts;
339 bool expire = false;
340 uint32_t pts_correction = avsync->delay * avsync->vsync_interval;
341
342 if (!fpts) {
343 if (avsync->last_frame) {
344 /* try to accumulate duration as PTS */
345 fpts = avsync->vpts + avsync->last_frame->duration;
346 } else {
347 fpts = avsync->vpts;
348 }
349 }
350 systime += pts_correction;
351
352 /* phase adjustment */
353 if (avsync->phase_set)
354 systime += avsync->phase;
355
Song Zhaobf35d4a2020-06-18 22:36:29 -0700356 log_trace("systime:%u phase:%u correct:%u", systime,
Song Zhao6859d412020-06-15 17:16:04 -0700357 avsync->phase_set?avsync->phase:0, pts_correction);
358 if (abs_diff(systime, fpts) > AV_DISCONTINUE_THREDHOLD_MIN &&
359 avsync->first_frame_toggled) {
360 /* ignore discontinity under pause */
361 if (avsync->paused && avsync->mode != AV_SYNC_MODE_PCR_MASTER)
362 return false;
363
364 log_warn("sync lost systime:%x fpts:%x", systime, fpts);
365 avsync->state = AV_SYNC_STAT_SYNC_LOST;
366 avsync->phase_set = false;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700367 if ((int)(systime - fpts) > 0) {
Song Zhao6859d412020-06-15 17:16:04 -0700368 if (frame->pts)
369 tsync_send_video_disc(avsync->session_id, frame->pts);
370 else if (avsync->mode != AV_SYNC_MODE_PCR_MASTER)
371 tsync_send_video_disc(avsync->session_id, frame->pts);
372 return false;
373 } else if (avsync->mode == AV_SYNC_MODE_PCR_MASTER) {
374 if (frame->pts)
375 tsync_send_video_disc(avsync->session_id, frame->pts);
376 else {
377 tsync_send_video_disc(avsync->session_id, fpts);
378 return true;
379 }
380 }
Song Zhao6859d412020-06-15 17:16:04 -0700381 }
382
Song Zhaoebb53bb2020-07-10 17:57:34 -0700383 expire = (int)(systime - fpts) >= 0;
Song Zhao6859d412020-06-15 17:16:04 -0700384
385 /* scatter the frame in different vsync whenever possible */
386 if (expire && next_frame && next_frame->pts && toggle_cnt) {
387 /* multi frame expired in current vsync but no frame in next vsync */
388 if (systime + avsync->vsync_interval < next_frame->pts) {
389 expire = false;
390 frame->hold_period++;
391 log_debug("unset expire systime:%d inter:%d next_pts:%d toggle_cnt:%d",
392 systime, avsync->vsync_interval, next_frame->pts, toggle_cnt);
393 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700394 } else if (!expire && next_frame && next_frame->pts && !toggle_cnt
395 && avsync->first_frame_toggled) {
Song Zhao6859d412020-06-15 17:16:04 -0700396 /* next vsync will have at least 2 frame expired */
397 if (systime + avsync->vsync_interval > next_frame->pts) {
398 expire = true;
399 log_debug("set expire systime:%d inter:%d next_pts:%d",
400 systime, avsync->vsync_interval, next_frame->pts);
401 }
402 }
403
404 correct_pattern(avsync->pattern_detector, frame, next_frame,
405 (avsync->last_frame?avsync->last_frame->hold_period:0),
406 avsync->last_holding_peroid, systime,
407 avsync->vsync_interval, &expire);
408
409 if (expire) {
410 avsync->vpts = fpts;
411 /* phase adjustment */
412 if (!avsync->phase_set) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700413 uint32_t phase_thres = avsync->vsync_interval / 4;
Song Zhao6859d412020-06-15 17:16:04 -0700414 //systime = tsync_get_pcr(avsync->session_id);
Song Zhaobf35d4a2020-06-18 22:36:29 -0700415 if ( systime > fpts && (systime - fpts) < phase_thres) {
416 /* too aligned to current VSYNC, separate them to 1/4 VSYNC */
417 avsync->phase += phase_thres - (systime - fpts);
418 avsync->phase_set = true;
419 log_info("adjust phase to %d", avsync->phase);
420 }
421 if (!avsync->phase_set && systime > fpts &&
422 systime < (fpts + avsync->vsync_interval) &&
423 (systime - fpts) > avsync->vsync_interval - phase_thres) {
424 /* too aligned to previous VSYNC, separate them to 1/4 VSYNC */
425 avsync->phase += phase_thres + fpts + avsync->vsync_interval - systime;
Song Zhao6859d412020-06-15 17:16:04 -0700426 avsync->phase_set = true;
427 log_info("adjust phase to %d", avsync->phase);
428 }
429 }
Song Zhaoebb53bb2020-07-10 17:57:34 -0700430
431 if (avsync->state != AV_SYNC_STAT_SYNC_SETUP)
432 log_info("sync setup");
433 avsync->state = AV_SYNC_STAT_SYNC_SETUP;
Song Zhao6859d412020-06-15 17:16:04 -0700434 }
435
436 return expire;
437}
438
439static void pattern_detect(struct av_sync_session* avsync, int cur_period, int last_period)
440{
441 log_trace("cur_period: %d last_period: %d", cur_period, last_period);
442 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P32, cur_period, last_period);
443 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P22, cur_period, last_period);
444 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P41, cur_period, last_period);
Song Zhao6859d412020-06-15 17:16:04 -0700445}