blob: fd4f276a21604330fd4acbd8e6b3c090e3d9b06f [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 }
95 if (vsync_interval < 750 || vsync_interval >= 3750) {
96 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
135 pthread_mutex_init(&avsync->lock, NULL);
Song Zhaobf35d4a2020-06-18 22:36:29 -0700136 log_info("mode: %d start_thres: %d delay: %d interval: %d done\n",
137 mode, start_thres, delay, vsync_interval);
Song Zhao6859d412020-06-15 17:16:04 -0700138 return avsync;
139}
140
141static int internal_stop(struct av_sync_session *avsync)
142{
143 int ret = 0;
144 struct vframe *frame;
145
146 pthread_mutex_lock(&avsync->lock);
147 if (avsync->state == AV_SYNC_STAT_INIT)
148 goto exit;
149
150 while (!dqueue_item(avsync->frame_q, (void **)&frame)) {
151 frame->free(frame);
152 }
153
154 avsync->state = AV_SYNC_STAT_INIT;
155exit:
156 pthread_mutex_unlock(&avsync->lock);
157 return ret;
158}
159
160/* destroy and detach from kernel session */
161void av_sync_destroy(void *sync)
162{
163 struct av_sync_session *avsync = (struct av_sync_session *)sync;
164
165 if (!avsync)
166 return;
167
168 log_info("begin");
169 if (avsync->state != AV_SYNC_STAT_INIT)
170 internal_stop(avsync);
171
172 /* all frames are freed */
173 //TODO: disconnect kernel session
Song Zhaobf35d4a2020-06-18 22:36:29 -0700174 tsync_set_pts_inc_mode(avsync->session_id, false);
Song Zhao6859d412020-06-15 17:16:04 -0700175 tsync_enable(avsync->session_id, false);
176 pthread_mutex_destroy(&avsync->lock);
177 destroy_q(avsync->frame_q);
178 destroy_pattern_detector(avsync->pattern_detector);
179 free(avsync);
180 log_info("done");
181}
182
183int av_sync_pause(void *sync, bool pause)
184{
185 struct av_sync_session *avsync = (struct av_sync_session *)sync;
186
187 if (!avsync)
188 return -1;
189
190 avsync->paused = pause;
191 tsync_send_video_pause(avsync->session_id, pause);
192 log_info("paused:%d\n", pause);
193 return 0;
194}
195
196int av_sync_push_frame(void *sync , struct vframe *frame)
197{
198 int ret;
199 struct av_sync_session *avsync = (struct av_sync_session *)sync;
200
201 if (!avsync)
202 return -1;
203
204 frame->hold_period = 0;
205 ret = queue_item(avsync->frame_q, frame);
206 if (avsync->state == AV_SYNC_STAT_INIT &&
207 queue_size(avsync->frame_q) >= avsync->start_thres) {
208 avsync->state = AV_SYNC_STAT_RUNNING;
209 log_info("state: init --> running");
210 }
211
212 if (ret)
213 log_error("%s queue fail:%d", ret);
214 return ret;
215
216}
217
218struct vframe *av_sync_pop_frame(void *sync)
219{
220 struct vframe *frame = NULL;
221 struct av_sync_session *avsync = (struct av_sync_session *)sync;
222 int toggle_cnt = 0;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700223 uint32_t systime;
Song Zhao6859d412020-06-15 17:16:04 -0700224
225 pthread_mutex_lock(&avsync->lock);
226 if (avsync->state == AV_SYNC_STAT_INIT)
227 goto exit;
228
229 if (!avsync->tsync_started) {
230 if (peek_item(avsync->frame_q, (void **)&frame, 0) || !frame) {
231 log_info("empty q");
232 goto exit;
233 }
234
Song Zhaobf35d4a2020-06-18 22:36:29 -0700235 if (tsync_enable(avsync->session_id, true))
236 log_error("enable tsync fail");
237 if (avsync->mode == AV_SYNC_MODE_VMASTER) {
238 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_VMASTER))
239 log_error("set vmaster mode fail");
240 if (tsync_set_pcr(avsync->session_id, frame->pts))
241 log_error("set pcr fail");
242 log_info("update pcr to: %u", frame->pts);
243 if (tsync_set_pts_inc_mode(avsync->session_id, true))
244 log_error("set inc mode fail");
245 if (tsync_set_video_peek_mode(avsync->session_id))
246 log_error("set peek mode fail");
247 } else if (avsync->mode == AV_SYNC_MODE_AMASTER) {
248 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_AMASTER))
249 log_error("set amaster mode fail");
250 } else {
251 //PCR master mode should be set alreay, but it won't hurt to set again.
252 if (tsync_set_mode(avsync->session_id, AV_SYNC_MODE_PCR_MASTER))
253 log_error("set pcrmaster mode fail");
254 }
255
Song Zhao6859d412020-06-15 17:16:04 -0700256 /* video start event */
Song Zhaobf35d4a2020-06-18 22:36:29 -0700257 if (tsync_send_video_start(avsync->session_id, frame->pts))
258 log_error("send video start fail");
259 else
260 log_info("video start %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700261 avsync->tsync_started = true;
262 }
263
Song Zhaobf35d4a2020-06-18 22:36:29 -0700264 systime = tsync_get_pcr(avsync->session_id);
Song Zhao6859d412020-06-15 17:16:04 -0700265 while (!peek_item(avsync->frame_q, (void **)&frame, 0)) {
266 struct vframe *next_frame = NULL;
267
268 peek_item(avsync->frame_q, (void **)&next_frame, 1);
269 if (next_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700270 log_debug("cur_f %u next_f %u", frame->pts, next_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700271 if (frame_expire(avsync, systime, frame, next_frame, toggle_cnt)) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700272 log_debug("cur_f %u expire", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700273 toggle_cnt++;
274
275 pattern_detect(avsync,
276 (avsync->last_frame?avsync->last_frame->hold_period:0),
277 avsync->last_holding_peroid);
278 if (avsync->last_frame)
279 avsync->last_holding_peroid = avsync->last_frame->hold_period;
280
281 dqueue_item(avsync->frame_q, (void **)&frame);
282 if (avsync->last_frame) {
283 /* free frame that are not for display */
284 if (toggle_cnt > 1)
285 avsync->last_frame->free(avsync->last_frame);
286 } else {
287 avsync->first_frame_toggled = true;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700288 log_info("first frame %u", frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700289 }
290 avsync->last_frame = frame;
291 } else
292 break;
293 }
294
295exit:
296 pthread_mutex_unlock(&avsync->lock);
297 if (avsync->last_frame)
Song Zhaobf35d4a2020-06-18 22:36:29 -0700298 log_debug("pop %u", avsync->last_frame->pts);
Song Zhao6859d412020-06-15 17:16:04 -0700299 else
300 log_debug("pop (nil)");
301 if (avsync->last_frame)
302 avsync->last_frame->hold_period++;
303 return avsync->last_frame;
304}
305
306void av_sync_update_vsync_interval(void *sync, pts90K vsync_interval)
307{
308 struct av_sync_session *avsync = (struct av_sync_session *)sync;
309
310 pthread_mutex_lock(&avsync->lock);
311 avsync->vsync_interval = vsync_interval;
312 if (avsync->state >= AV_SYNC_STAT_RUNNING) {
313 reset_pattern(avsync->pattern_detector);
314 avsync->phase_set = false;
315 }
316 pthread_mutex_unlock(&avsync->lock);
317}
318
319static inline uint32_t abs_diff(uint32_t a, uint32_t b)
320{
321 return a > b ? a - b : b - a;
322}
323
324static bool frame_expire(struct av_sync_session* avsync,
325 uint32_t systime,
326 struct vframe * frame,
327 struct vframe * next_frame,
328 int toggle_cnt)
329{
330 uint32_t fpts = frame->pts;
331 bool expire = false;
332 uint32_t pts_correction = avsync->delay * avsync->vsync_interval;
333
334 if (!fpts) {
335 if (avsync->last_frame) {
336 /* try to accumulate duration as PTS */
337 fpts = avsync->vpts + avsync->last_frame->duration;
338 } else {
339 fpts = avsync->vpts;
340 }
341 }
342 systime += pts_correction;
343
344 /* phase adjustment */
345 if (avsync->phase_set)
346 systime += avsync->phase;
347
Song Zhaobf35d4a2020-06-18 22:36:29 -0700348 log_trace("systime:%u phase:%u correct:%u", systime,
Song Zhao6859d412020-06-15 17:16:04 -0700349 avsync->phase_set?avsync->phase:0, pts_correction);
350 if (abs_diff(systime, fpts) > AV_DISCONTINUE_THREDHOLD_MIN &&
351 avsync->first_frame_toggled) {
352 /* ignore discontinity under pause */
353 if (avsync->paused && avsync->mode != AV_SYNC_MODE_PCR_MASTER)
354 return false;
355
356 log_warn("sync lost systime:%x fpts:%x", systime, fpts);
357 avsync->state = AV_SYNC_STAT_SYNC_LOST;
358 avsync->phase_set = false;
Song Zhaobf35d4a2020-06-18 22:36:29 -0700359 if ((int)(systime - fpts) > 0) {
Song Zhao6859d412020-06-15 17:16:04 -0700360 if (frame->pts)
361 tsync_send_video_disc(avsync->session_id, frame->pts);
362 else if (avsync->mode != AV_SYNC_MODE_PCR_MASTER)
363 tsync_send_video_disc(avsync->session_id, frame->pts);
364 return false;
365 } else if (avsync->mode == AV_SYNC_MODE_PCR_MASTER) {
366 if (frame->pts)
367 tsync_send_video_disc(avsync->session_id, frame->pts);
368 else {
369 tsync_send_video_disc(avsync->session_id, fpts);
370 return true;
371 }
372 }
373 } else {
374 if (avsync->state != AV_SYNC_STAT_SYNC_SETUP)
375 log_info("sync setup");
376 avsync->state = AV_SYNC_STAT_SYNC_SETUP;
377 }
378
379 expire = (systime >= fpts);
380
381 /* scatter the frame in different vsync whenever possible */
382 if (expire && next_frame && next_frame->pts && toggle_cnt) {
383 /* multi frame expired in current vsync but no frame in next vsync */
384 if (systime + avsync->vsync_interval < next_frame->pts) {
385 expire = false;
386 frame->hold_period++;
387 log_debug("unset expire systime:%d inter:%d next_pts:%d toggle_cnt:%d",
388 systime, avsync->vsync_interval, next_frame->pts, toggle_cnt);
389 }
390 } else if (!expire && next_frame && next_frame->pts && !toggle_cnt) {
391 /* next vsync will have at least 2 frame expired */
392 if (systime + avsync->vsync_interval > next_frame->pts) {
393 expire = true;
394 log_debug("set expire systime:%d inter:%d next_pts:%d",
395 systime, avsync->vsync_interval, next_frame->pts);
396 }
397 }
398
399 correct_pattern(avsync->pattern_detector, frame, next_frame,
400 (avsync->last_frame?avsync->last_frame->hold_period:0),
401 avsync->last_holding_peroid, systime,
402 avsync->vsync_interval, &expire);
403
404 if (expire) {
405 avsync->vpts = fpts;
406 /* phase adjustment */
407 if (!avsync->phase_set) {
Song Zhaobf35d4a2020-06-18 22:36:29 -0700408 uint32_t phase_thres = avsync->vsync_interval / 4;
Song Zhao6859d412020-06-15 17:16:04 -0700409 //systime = tsync_get_pcr(avsync->session_id);
Song Zhaobf35d4a2020-06-18 22:36:29 -0700410 if ( systime > fpts && (systime - fpts) < phase_thres) {
411 /* too aligned to current VSYNC, separate them to 1/4 VSYNC */
412 avsync->phase += phase_thres - (systime - fpts);
413 avsync->phase_set = true;
414 log_info("adjust phase to %d", avsync->phase);
415 }
416 if (!avsync->phase_set && systime > fpts &&
417 systime < (fpts + avsync->vsync_interval) &&
418 (systime - fpts) > avsync->vsync_interval - phase_thres) {
419 /* too aligned to previous VSYNC, separate them to 1/4 VSYNC */
420 avsync->phase += phase_thres + fpts + avsync->vsync_interval - systime;
Song Zhao6859d412020-06-15 17:16:04 -0700421 avsync->phase_set = true;
422 log_info("adjust phase to %d", avsync->phase);
423 }
424 }
425 }
426
427 return expire;
428}
429
430static void pattern_detect(struct av_sync_session* avsync, int cur_period, int last_period)
431{
432 log_trace("cur_period: %d last_period: %d", cur_period, last_period);
433 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P32, cur_period, last_period);
434 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P22, cur_period, last_period);
435 detect_pattern(avsync->pattern_detector, AV_SYNC_FRAME_P41, cur_period, last_period);
Song Zhao6859d412020-06-15 17:16:04 -0700436}