| /* |
| * Copyright (c) 2020 Amlogic, Inc. All rights reserved. |
| * |
| * This source code is subject to the terms and conditions defined in the |
| * file 'LICENSE' which is part of this source code package. |
| * |
| * Description: |
| * User space AV sync module. |
| * |
| * Author: song.zhao@amlogic.com |
| */ |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| #include <linux/types.h> |
| #include <string.h> |
| #include "msync.h" |
| #include "aml_avsync_log.h" |
| #include "msync_util.h" |
| |
| #define MSYNC_DEV "/dev/aml_msync" |
| |
| int msync_create_session() |
| { |
| int fd; |
| |
| fd = open(MSYNC_DEV, O_RDONLY | O_CLOEXEC); |
| if (fd < 0) { |
| log_error("%s errno:%d", MSYNC_DEV, errno); |
| return -1; |
| } |
| return fd; |
| } |
| |
| void msync_destory_session(int fd) |
| { |
| close(fd); |
| } |
| |
| int msync_session_set_mode(int fd, enum sync_mode mode) |
| { |
| int rc; |
| uint32_t kmode; |
| |
| if (mode == AV_SYNC_MODE_VMASTER) |
| kmode = AVS_MODE_V_MASTER; |
| else if (mode == AV_SYNC_MODE_AMASTER) |
| kmode = AVS_MODE_A_MASTER; |
| else if (mode == AV_SYNC_MODE_PCR_MASTER) |
| kmode = AVS_MODE_PCR_MASTER; |
| else if (mode == AV_SYNC_MODE_IPTV) |
| kmode = AVS_MODE_IPTV; |
| else if (mode == AV_SYNC_MODE_FREE_RUN) |
| kmode = AVS_MODE_FREE_RUN; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_MODE, &kmode); |
| if (rc) |
| log_error("session[%d] set mode errno:%d", fd, errno); |
| return rc; |
| } |
| |
| int msync_session_get_mode(int fd, enum sync_mode *mode) |
| { |
| int rc; |
| uint32_t kmode; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_MODE, &kmode); |
| if (rc) { |
| log_error("session[%d] set mode errno:%d", fd, errno); |
| return rc; |
| } |
| |
| if (kmode == AVS_MODE_V_MASTER) |
| *mode = AV_SYNC_MODE_VMASTER; |
| else if (kmode == AVS_MODE_A_MASTER) |
| *mode = AV_SYNC_MODE_AMASTER; |
| else if (kmode == AVS_MODE_PCR_MASTER) |
| *mode = AV_SYNC_MODE_PCR_MASTER; |
| else if (kmode == AVS_MODE_IPTV) |
| *mode = AV_SYNC_MODE_IPTV; |
| else if (kmode == AVS_MODE_FREE_RUN) |
| *mode = AV_SYNC_MODE_FREE_RUN; |
| |
| return rc; |
| } |
| |
| int msync_session_get_start_policy(int fd, uint32_t *policy, int *timeout) |
| { |
| int rc; |
| struct ker_start_policy kpolicy; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_START_POLICY, &kpolicy); |
| if (rc) |
| log_error("session[%d] get start policy errno:%d", fd, errno); |
| |
| if (kpolicy.policy == AMSYNC_START_V_FIRST) |
| *policy = AV_SYNC_START_V_FIRST; |
| else if (kpolicy.policy == AMSYNC_START_A_FIRST) |
| *policy = AV_SYNC_START_A_FIRST; |
| else if (kpolicy.policy == AMSYNC_START_ASAP) |
| *policy = AV_SYNC_START_ASAP; |
| else if (kpolicy.policy == AMSYNC_START_ALIGN) |
| *policy = AV_SYNC_START_ALIGN; |
| else |
| *policy = AV_SYNC_START_NONE; |
| |
| if (0 == rc) |
| *timeout = kpolicy.timeout; |
| |
| return rc; |
| } |
| int msync_session_set_start_policy(int fd, uint32_t policy, int timeout) |
| { |
| int rc; |
| struct ker_start_policy kpolicy; |
| |
| if (policy == AV_SYNC_START_V_FIRST) |
| kpolicy.policy = AMSYNC_START_V_FIRST; |
| else if (policy == AV_SYNC_START_A_FIRST) |
| kpolicy.policy = AMSYNC_START_A_FIRST; |
| else if (policy == AV_SYNC_START_ASAP) |
| kpolicy.policy = AMSYNC_START_ASAP; |
| else if (policy == AV_SYNC_START_ALIGN) |
| kpolicy.policy = AMSYNC_START_ALIGN; |
| else |
| return -1; |
| |
| kpolicy.timeout = timeout; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_START_POLICY, &kpolicy); |
| if (rc) |
| log_error("session[%d] set start policy errno:%d", fd, errno); |
| return rc; |
| } |
| |
| static int msync_session_set_event(int fd, enum avs_event event, uint32_t value) |
| { |
| struct session_event sevent; |
| int rc; |
| |
| sevent.event = event; |
| sevent.value = value; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SEND_EVENT, &sevent); |
| if (rc) |
| log_error("session[%d] send %d errno:%d", fd, event, errno); |
| return rc; |
| } |
| |
| |
| int msync_session_set_pause(int fd, bool pause) |
| { |
| if (pause) |
| return msync_session_set_event(fd, AVS_PAUSE, 0); |
| else |
| return msync_session_set_event(fd, AVS_RESUME, 0); |
| } |
| |
| int msync_session_get_wall(int fd, uint32_t *wall, uint32_t *interval) |
| { |
| int rc; |
| struct pts_wall pwall; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_WALL, &pwall); |
| if (rc) |
| log_error("session[%d] get wall errno:%d", fd, errno); |
| |
| *wall = pwall.wall_clock; |
| if (interval) |
| *interval = pwall.interval; |
| return rc; |
| } |
| |
| int msync_session_set_video_start(int fd, pts90K pts) |
| { |
| return msync_session_set_event(fd, AVS_VIDEO_START, pts); |
| } |
| |
| int msync_session_set_audio_start(int fd, pts90K pts, pts90K delay, uint32_t *mode) |
| { |
| struct audio_start start; |
| int rc; |
| |
| if (!mode) |
| return -EINVAL; |
| |
| start.pts = pts; |
| start.delay = delay; |
| start.mode = 0; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_AUDIO_START, &start); |
| if (rc) |
| log_error("session[%d] audio start errno:%d", fd, errno); |
| else |
| *mode = start.mode; |
| |
| return rc; |
| } |
| |
| int msync_session_set_video_dis(int fd, pts90K pts) |
| { |
| return msync_session_set_event(fd, AVS_VIDEO_TSTAMP_DISCONTINUITY, pts); |
| } |
| |
| int msync_session_set_audio_dis(int fd, pts90K pts) |
| { |
| return msync_session_set_event(fd, AVS_AUDIO_TSTAMP_DISCONTINUITY, pts); |
| } |
| |
| int msync_session_set_rate(int fd, float speed) |
| { |
| uint32_t krate = 1000*speed; |
| int rc; |
| |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_RATE, &krate); |
| if (rc) |
| log_error("fd[%d] set rate errno:%d", fd, errno); |
| return rc; |
| } |
| |
| int msync_session_get_rate(int fd, float *speed) |
| { |
| uint32_t krate; |
| int rc; |
| |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_RATE, &krate); |
| if (rc) { |
| log_error("fd[%d] get rate errno:%d", fd, errno); |
| return rc; |
| } |
| *speed = (float)krate/1000; |
| return 0; |
| } |
| |
| int msync_session_set_name(int fd, const char* name) |
| { |
| int rc; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_NAME, name); |
| if (rc) |
| log_error("session[%d] set name errno:%d", fd, errno); |
| return rc; |
| } |
| |
| int msync_session_update_vpts(int fd, uint32_t system, uint32_t pts, uint32_t delay) |
| { |
| int rc; |
| struct pts_tri ts; |
| |
| ts.wall_clock = system; |
| ts.pts = pts; |
| ts.delay = delay; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_V_TS, &ts); |
| if (rc) |
| log_error("session[%d] set vts errno:%d", fd, errno); |
| return rc; |
| } |
| |
| int msync_session_update_apts(int fd, uint32_t system, uint32_t pts, uint32_t delay) |
| { |
| int rc; |
| struct pts_tri ts; |
| |
| ts.wall_clock = system; |
| ts.pts = pts; |
| ts.delay = delay; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_A_TS, &ts); |
| if (rc) |
| log_error("session[%d] set ats errno:%d", fd, errno); |
| return rc; |
| } |
| |
| int msync_session_set_audio_stop(int fd) |
| { |
| return msync_session_set_event(fd, AVS_AUDIO_STOP, 0); |
| } |
| |
| int msync_session_set_video_stop(int fd) |
| { |
| return msync_session_set_event(fd, AVS_VIDEO_STOP, 0); |
| } |
| |
| int msync_session_get_stat (int fd, enum sync_mode *mode, |
| bool *v_active, bool *a_active, bool *v_timeout, |
| bool *a_switch) |
| { |
| int rc; |
| struct session_sync_stat stat; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_SYNC_STAT, &stat); |
| if (rc) { |
| log_error("fd[%d] get state errno:%d", fd, errno); |
| return rc; |
| } |
| |
| switch (stat.mode) { |
| case AVS_MODE_A_MASTER: |
| *mode = AV_SYNC_MODE_AMASTER; |
| break; |
| case AVS_MODE_V_MASTER: |
| *mode = AV_SYNC_MODE_VMASTER; |
| break; |
| case AVS_MODE_PCR_MASTER: |
| *mode = AV_SYNC_MODE_PCR_MASTER; |
| break; |
| case AVS_MODE_IPTV: |
| *mode = AV_SYNC_MODE_IPTV; |
| break; |
| case AVS_MODE_FREE_RUN: |
| *mode = AV_SYNC_MODE_FREE_RUN; |
| break; |
| } |
| if (v_active) |
| *v_active = stat.v_active; |
| if (a_active) |
| *a_active = stat.a_active; |
| if (v_timeout) |
| *v_timeout = stat.v_timeout; |
| if (a_switch) |
| *a_switch = stat.audio_switch; |
| return rc; |
| } |
| |
| bool msync_clock_started(int fd) |
| { |
| uint32_t start = 0; |
| int rc; |
| |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_CLOCK_START, &start); |
| if (rc) |
| log_error("session[%d] set clock start errno:%d", fd, errno); |
| return start != 0; |
| } |
| |
| int msync_session_set_pcr(int fd, pts90K pts, uint64_t mono_clock) |
| { |
| int rc; |
| struct pcr_pair pcr; |
| |
| pcr.pts = pts; |
| pcr.mono_clock = mono_clock; |
| rc = ioctl(fd, AMSYNCS_IOC_SET_PCR, &pcr); |
| if (rc) |
| log_error("session[%d] set pcr %u errno:%d", fd, pcr, errno); |
| |
| return rc; |
| } |
| |
| int msync_session_get_pcr(int fd, pts90K *pts, uint64_t *mono_clock) |
| { |
| int rc; |
| struct pcr_pair pcr; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_PCR, &pcr); |
| if (rc) |
| log_error("session[%d] set pcr %u errno:%d", fd, pcr, errno); |
| else { |
| *pts = pcr.pts; |
| *mono_clock = pcr.mono_clock; |
| } |
| |
| return rc; |
| } |
| |
| int msync_session_get_debug_mode(int fd, struct session_debug *debug) |
| { |
| int rc; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_DEBUG_MODE, debug); |
| if (rc) |
| log_error("session[%d] set debug mode errno:%d", fd, errno); |
| |
| return rc; |
| } |
| |
| int msync_session_set_audio_switch(int fd, bool start) |
| { |
| log_info("audio switch set to %d", start); |
| return msync_session_set_event(fd, AVS_AUDIO_SWITCH, start); |
| } |
| |
| int msync_session_set_clock_dev(int fd, int32_t ppm) |
| { |
| int rc; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_SET_CLK_DEV, &ppm); |
| if (rc) |
| log_error("session[%d] set clk dev errno:%d", fd, errno); |
| return rc; |
| } |
| |
| int msync_session_get_clock_dev(int fd, int32_t *ppm) |
| { |
| int rc; |
| int dev; |
| |
| rc = ioctl(fd, AMSYNCS_IOC_GET_CLK_DEV, &dev); |
| if (rc) |
| log_error("session[%d] get clk dev errno:%d", fd, errno); |
| else |
| *ppm = dev; |
| return rc; |
| } |
| |
| static int get_sysfs_uint32(const char *path, uint32_t *value) |
| { |
| int fd; |
| char valstr[64]; |
| uint32_t val = 0; |
| |
| fd = open(path, O_RDONLY); |
| if (fd >= 0) { |
| memset(valstr, 0, 64); |
| read(fd, valstr, 64 - 1); |
| valstr[strlen(valstr)] = '\0'; |
| close(fd); |
| } else { |
| log_error("unable to open file %s\n", path); |
| return -1; |
| } |
| if (sscanf(valstr, "%u", &val) < 1) { |
| log_error("unable to get pts from: %s", valstr); |
| return -1; |
| } |
| *value = val; |
| return 0; |
| } |
| |
| int msync_session_get_disc_thres(int session_id, uint32_t *min, uint32_t *max) |
| { |
| char name[64]; |
| |
| if (snprintf(name, sizeof(name), |
| "/sys/class/avsync_session%d/disc_thres_min", session_id) < 0) |
| return -1; |
| if (get_sysfs_uint32(name, min)) |
| return -1; |
| |
| if (snprintf(name, sizeof(name), |
| "/sys/class/avsync_session%d/disc_thres_max", session_id) < 0) |
| return -1; |
| if (get_sysfs_uint32(name, max)) |
| return -1; |
| |
| return 0; |
| } |