avsync-lib: support PCR master mode [2/4]

Change-Id: I7203cfa73f7e6a202218514b6272aeed1eee8841
diff --git a/src/aml_avsync.h b/src/aml_avsync.h
index 12bfa82..4c595aa 100644
--- a/src/aml_avsync.h
+++ b/src/aml_avsync.h
@@ -62,6 +62,7 @@
 typedef enum {
     AV_SYNC_ASTART_SYNC = 0,
     AV_SYNC_ASTART_ASYNC,
+    AV_SYNC_ASTART_AGAIN,
     AV_SYNC_ASTART_ERR
 } avs_start_ret;
 
@@ -80,6 +81,7 @@
     /* delta between apts and ideal render position
      * positive means apts is behind wall clock
      * negative means apts is ahead of wall clock
+     * 0 is only valid for AV_SYNC_AA_DROP, drop current chunk
      */
     int delta;
 };
@@ -216,7 +218,7 @@
 struct vframe *av_sync_pop_frame(void *sync);
 
 /* Audio start control. Audio render need to check return value and
- * do sync or async start.
+ * do sync or async start. NOT thread safe.
  * Params:
  *   @sync: AV sync module handle
  *   @pts: first audio pts
@@ -226,6 +228,7 @@
  * Return:
  *   AV_SYNC_ASTART_SYNC, audio can start render ASAP. No need to wait for callback.
  *   AV_SYNC_ASTART_ASYNC, audio need to block until callback is triggered.
+ *   AV_SYNC_ASTART_AGAIN, discard current audio chunk and call this API again.
  *   AV_SYNC_ASTART_ERR, something bad happens
  */
 avs_start_ret av_sync_audio_start(
@@ -236,6 +239,7 @@
     void *priv);
 
 /* Audio render policy. Indicate render action and the difference from ideal position
+ * NOT thread safe.
  * Params:
  *   @sync: AV sync module handle
  *   @pts: curent audio pts (considering delay)
@@ -303,12 +307,22 @@
  * Params:
  *   @sync: AV sync module handle
  *   @pts: PCR clock
+ *   @mono_clock: system monotonic clock receiving the pts
  * Return:
  *   0 for OK, or error code
  */
-int av_sync_set_pcr_clock(void *sync, pts90K pts);
+int av_sync_set_pcr_clock(void *sync, pts90K pts, uint64_t mono_clock);
 
-int av_sync_get_pcr_clock(void *sync, pts90K *pts);
+/* Get PCR clock pair.
+ * Use for clock recovery
+ * Params:
+ *   @sync: AV sync module handle
+ *   @pts: PCR clock pointer
+ *   @mono_clock: system monotonic clock pointer receiving the pts
+ * Return:
+ *   0 for OK, or error code
+ */
+int av_sync_get_pcr_clock(void *sync, pts90K *pts, uint64_t *mono_clock);
 
 /* get wall clock
  * Params:
diff --git a/src/avsync.c b/src/avsync.c
index 52adfa5..e30dc51 100644
--- a/src/avsync.c
+++ b/src/avsync.c
@@ -85,14 +85,6 @@
 
     float speed;
 
-#if 0
-    /*pip sync, remove after multi instance is supported*/
-    struct timeval base_sys_time;
-    struct timeval pause_start;
-    uint64_t pause_duration;
-    pts90K first_pts;
-#endif
-
     /* pause pts */
     pts90K pause_pts;
     pause_pts_done pause_pts_cb;
@@ -110,14 +102,20 @@
 
     /* error detection */
     uint32_t last_poptime;
+    uint32_t outlier_cnt;
 };
 
 #define MAX_FRAME_NUM 32
 #define DEFAULT_START_THRESHOLD 2
 #define TIME_UNIT90K    (90000)
-#define AV_DISCONTINUE_THREDHOLD_MIN (TIME_UNIT90K / 3)
-#define A_ADJ_THREDHOLD (TIME_UNIT90K/10)
+#define AV_DISC_THRES_MIN (TIME_UNIT90K * 3)
+#define A_ADJ_THREDHOLD_HB (TIME_UNIT90K/10)
+#define A_ADJ_THREDHOLD_LB (TIME_UNIT90K/40)
 #define SYNC_LOST_PRINT_THRESHOLD 10000000 //10 seconds In micro seconds
+#define LIVE_MODE(mode) ((mode) == AV_SYNC_MODE_PCR_MASTER || (mode) == AV_SYNC_MODE_IPTV)
+
+#define STREAM_DISC_THRES (TIME_UNIT90K * 10)
+#define OUTLIER_MAX_CNT 8
 
 static uint64_t time_diff (struct timeval *b, struct timeval *a);
 static bool frame_expire(struct av_sync_session* avsync,
@@ -400,7 +398,7 @@
     }
 
     if (!peek_item(avsync->frame_q, (void **)&prev, 0)) {
-        if (prev->pts == frame->pts) {
+        if (prev->pts == frame->pts && avsync->mode == AV_SYNC_MODE_AMASTER) {
             dqueue_item(avsync->frame_q, (void **)&prev);
             prev->free(prev);
             log_info ("[%d]drop frame with same pts %u", avsync->session_id, frame->pts);
@@ -433,7 +431,7 @@
 
     pthread_mutex_lock(&avsync->lock);
     if (avsync->state == AV_SYNC_STAT_INIT) {
-        log_trace("[%d]in state INIT", avsync->session_id);
+        log_info("[%d]in state INIT", avsync->session_id);
         goto exit;
     }
 
@@ -569,6 +567,9 @@
     bool expire = false;
     uint32_t pts_correction = avsync->delay * interval;
 
+    if (avsync->mode == AV_SYNC_MODE_FREE_RUN)
+        return true;
+
     if (avsync->paused && avsync->pause_pts == AV_SYNC_INVALID_PAUSE_PTS)
         return false;
 
@@ -592,7 +593,7 @@
     log_trace("[%d]systime:%u phase:%u correct:%u fpts:%u",
             avsync->session_id, systime,
             avsync->phase_set?avsync->phase:0, pts_correction, fpts);
-    if (abs_diff(systime, fpts) > AV_DISCONTINUE_THREDHOLD_MIN &&
+    if (abs_diff(systime, fpts) > AV_DISC_THRES_MIN &&
             avsync->first_frame_toggled) {
         /* ignore discontinity under pause */
         if (avsync->paused)
@@ -613,25 +614,36 @@
             } else
                 avsync->sync_lost_cnt++;
         }
+
+        if (avsync->state == AV_SYNC_STAT_SYNC_SETUP &&
+                LIVE_MODE(avsync->mode) &&
+                abs_diff(systime, fpts) > STREAM_DISC_THRES) {
+            /* outlier by stream error */
+            avsync->outlier_cnt++;
+            if (avsync->outlier_cnt < OUTLIER_MAX_CNT) {
+                log_info("render outlier %u", fpts);
+                return true;
+            }
+        }
+
+        avsync->outlier_cnt = 0;
         avsync->state = AV_SYNC_STAT_SYNC_LOST;
         avsync->phase_set = false;
         reset_pattern(avsync->pattern_detector);
         if ((int)(systime - fpts) > 0) {
-            if (frame->pts && avsync->mode == AV_SYNC_MODE_VMASTER) {
+            if (LIVE_MODE(avsync->mode)) {
                 log_info ("[%d]video disc %u --> %u",
                     avsync->session_id, systime, fpts);
                 msync_session_set_video_dis(avsync->fd, frame->pts);
             }
-            /*catch up PCR */
+            /* catch up PCR */
             return true;
-        } else if (avsync->mode == AV_SYNC_MODE_PCR_MASTER ||
-                avsync->mode == AV_SYNC_MODE_IPTV) {
+        } else if (LIVE_MODE(avsync->mode)) {
             /* vpts wrapping */
-            if (frame->pts)
-                msync_session_set_video_dis(avsync->fd, frame->pts);
-            else
-                msync_session_set_video_dis(avsync->fd, fpts);
-            return true;
+            log_info ("[%d]video disc %u --> %u",
+                    avsync->session_id, systime, fpts);
+            msync_session_set_video_dis(avsync->fd, frame->pts);
+            return false;
         }
     }
 
@@ -818,13 +830,19 @@
     if (msync_session_set_audio_start(avsync->fd, pts, delay, &start_mode))
         log_error("[%d]fail to set audio start", avsync->session_id);
 
-    avsync->state = AV_SYNC_STAT_RUNNING;
     if (start_mode == AVS_START_SYNC) {
         ret = AV_SYNC_ASTART_SYNC;
         avsync->session_started = true;
         avsync->state = AV_SYNC_STAT_SYNC_SETUP;
-    } else if (start_mode == AVS_START_ASYNC)
+    } else if (start_mode == AVS_START_ASYNC) {
         ret = AV_SYNC_ASTART_ASYNC;
+        avsync->state = AV_SYNC_STAT_RUNNING;
+    } else if (start_mode == AVS_START_AGAIN) {
+        ret = AV_SYNC_ASTART_AGAIN;
+    }
+
+    if (ret == AV_SYNC_ASTART_AGAIN)
+        goto exit;
 
     if (avsync->mode == AV_SYNC_MODE_AMASTER) {
         create_poll_t = true;
@@ -836,10 +854,10 @@
             avsync->audio_start = cb;
             avsync->audio_start_priv = priv;
         }
-    } else if (avsync->mode == AV_SYNC_MODE_PCR_MASTER || start_mode == AV_SYNC_MODE_IPTV)
+    } else if (LIVE_MODE(avsync->mode))
         create_poll_t = true;
 
-    if (create_poll_t) {
+    if (create_poll_t && !avsync->poll_thread) {
         int ret;
 
         log_info("[%d]start poll thread", avsync->session_id);
@@ -850,7 +868,13 @@
             return AV_SYNC_ASTART_ERR;
         }
     }
-
+    if (LIVE_MODE(avsync->mode)) {
+        uint32_t systime;
+        msync_session_get_wall(avsync->fd, &systime, NULL);
+        log_info("[%d]return %u w %u pts %u d %u",
+                avsync->session_id, ret, systime, pts, delay);
+    }
+exit:
     log_info("[%d]return %u", avsync->session_id, ret);
     return ret;
 }
@@ -874,18 +898,59 @@
         goto done;
     }
 
+    /* stopping procedure, unblock audio rendering */
+    if (avsync->mode == AV_SYNC_MODE_PCR_MASTER &&
+            avsync->active_mode == AV_SYNC_MODE_FREE_RUN) {
+        action = AV_SYNC_AA_DROP;
+        goto done;
+    }
+
     msync_session_get_wall(avsync->fd, &systime, NULL);
-    if (abs_diff(systime, pts) < A_ADJ_THREDHOLD) {
+
+    if (avsync->state == AV_SYNC_STAT_SYNC_SETUP &&
+            LIVE_MODE(avsync->mode) &&
+            abs_diff(systime, pts) > STREAM_DISC_THRES) {
+        /* outlier by stream error */
+        avsync->outlier_cnt++;
+        if (avsync->outlier_cnt > OUTLIER_MAX_CNT) {
+            /* treat as disc, just drop current frame */
+            avsync->state = AV_SYNC_STAT_SYNC_LOST;
+            avsync->outlier_cnt = 0;
+            action = AV_SYNC_AA_DROP;
+            systime = pts;
+            msync_session_set_audio_dis(avsync->fd, pts);
+            goto done;
+        }
+        log_info("[%d]ignore outlier %u", avsync->session_id, pts);
+        pts = systime;
+        action = AV_SYNC_AA_RENDER;
+        goto done;
+    }
+
+    avsync->outlier_cnt = 0;
+    /* low bound from sync_lost to sync_setup */
+    if (abs_diff(systime, pts) < A_ADJ_THREDHOLD_LB) {
+        avsync->state = AV_SYNC_STAT_SYNC_SETUP;
+        action = AV_SYNC_AA_RENDER;
+        goto done;
+    }
+
+    /* high bound of sync_setup */
+    if (abs_diff(systime, pts) < A_ADJ_THREDHOLD_HB &&
+            avsync->state != AV_SYNC_STAT_SYNC_LOST) {
+        avsync->state = AV_SYNC_STAT_SYNC_SETUP;
         action = AV_SYNC_AA_RENDER;
         goto done;
     }
 
     if ((int)(systime - pts) > 0) {
+        avsync->state = AV_SYNC_STAT_SYNC_LOST;
         action = AV_SYNC_AA_DROP;
         goto done;
     }
 
     if ((int)(systime - pts) < 0) {
+        avsync->state = AV_SYNC_STAT_SYNC_LOST;
         action = AV_SYNC_AA_INSERT;
         goto done;
     }
@@ -896,8 +961,11 @@
     if (action == AV_SYNC_AA_RENDER) {
         avsync->apts = pts;
         msync_session_update_apts(avsync->fd, systime, pts, 0);
+        log_debug("[%d]return %d sys %u - pts %u = %d",
+                avsync->session_id, action, systime, pts, systime - pts);
     } else {
-        log_info("[%d]return %d sys %u pts %u", avsync->session_id, action, systime, pts);
+        log_info("[%d]return %d sys %u - pts %u = %d",
+                avsync->session_id, action, systime, pts, systime - pts);
     }
 
     return ret;
@@ -943,6 +1011,19 @@
             }
             avsync->speed = speed;
         }
+    } else if (avsync->active_mode == AV_SYNC_MODE_PCR_MASTER) {
+        struct session_debug debug;
+        if (!msync_session_get_debug_mode(avsync->fd, &debug)) {
+            if (debug.debug_freerun) {
+                avsync->backup_mode = avsync->mode;
+                avsync->mode = AV_SYNC_MODE_FREE_RUN;
+                log_warn("[%d]audio to freerun mode", avsync->session_id);
+            } else {
+                avsync->mode = avsync->backup_mode;
+                log_warn("[%d]audio back to mode %d",
+                        avsync->session_id, avsync->mode);
+            }
+        }
     }
 }
 
@@ -951,6 +1032,19 @@
 {
     log_info("[%d]amode mode %d %d v/a %d/%d", avsync->session_id,
             avsync->active_mode, avsync->mode, v_active, a_active);
+    if (avsync->active_mode == AV_SYNC_MODE_PCR_MASTER) {
+        struct session_debug debug;
+        if (!msync_session_get_debug_mode(avsync->fd, &debug)) {
+            if (debug.debug_freerun) {
+                avsync->backup_mode = avsync->mode;
+                avsync->mode = AV_SYNC_MODE_FREE_RUN;
+                log_warn("[%d]video to freerun mode", avsync->session_id);
+            } else
+                avsync->mode = avsync->backup_mode;
+                log_warn("[%d]video back to mode %d",
+                        avsync->session_id, avsync->mode);
+        }
+    }
 }
 
 static void * poll_thread(void * arg)
@@ -981,7 +1075,9 @@
         if (pfd.revents & POLLERR)
             log_error("[%d]POLLERR received", avsync->session_id);
 
-        /* mode change */
+        /* mode change. Non-exclusive wait so all the processes
+         * shall be woken up
+         */
         if (pfd.revents & POLLPRI) {
             bool v_active, a_active, v_timeout;
 
@@ -999,7 +1095,7 @@
     return NULL;
 }
 
-int av_sync_set_pcr_clock(void *sync, pts90K pts)
+int av_sync_set_pcr_clock(void *sync, pts90K pts, uint64_t mono_clock)
 {
     struct av_sync_session *avsync = (struct av_sync_session *)sync;
 
@@ -1009,10 +1105,10 @@
     if (avsync->type != AV_SYNC_TYPE_PCR)
         return -2;
 
-    return msync_session_set_pcr(avsync->fd, pts);
+    return msync_session_set_pcr(avsync->fd, pts, mono_clock);
 }
 
-int av_sync_get_pcr_clock(void *sync, pts90K *pts)
+int av_sync_get_pcr_clock(void *sync, pts90K *pts, uint64_t * mono_clock)
 {
     struct av_sync_session *avsync = (struct av_sync_session *)sync;
 
@@ -1022,7 +1118,7 @@
     if (avsync->type != AV_SYNC_TYPE_PCR)
         return -2;
 
-    return msync_session_get_pcr(avsync->fd, pts);
+    return msync_session_get_pcr(avsync->fd, pts, mono_clock);
 }
 
 int av_sync_set_session_name(void *sync, const char *name)
diff --git a/src/msync.h b/src/msync.h
index 8487d5f..f7bcd68 100644
--- a/src/msync.h
+++ b/src/msync.h
@@ -25,6 +25,11 @@
 	uint32_t delay;
 };
 
+struct pcr_pair {
+	uint32_t pts;
+	uint64_t mono_clock;
+};
+
 struct pts_wall {
 	uint32_t wall_clock;
 	uint32_t interval;
@@ -61,6 +66,7 @@
 enum avs_astart_mode {
     AVS_START_SYNC = 0,
     AVS_START_ASYNC,
+    AVS_START_AGAIN,
     AVS_START_MAX
 };
 
@@ -79,6 +85,12 @@
 	uint32_t value;
 };
 
+struct session_debug {
+        uint32_t debug_freerun;
+        uint32_t pcr_init_flag;
+        uint32_t pcr_init_mode;
+};
+
 #define AVS_INVALID_PTS 0xFFFFFFFFUL
 
 #define AMSYNC_START_V_FIRST 0x1
@@ -104,8 +116,8 @@
 #define AMSYNCS_IOC_SEND_EVENT		_IOWR((_A_M_SS), 0x08, struct session_event)
 //For PCR/IPTV mode only
 #define AMSYNCS_IOC_GET_SYNC_STAT	_IOWR((_A_M_SS), 0x09, struct session_sync_stat)
-#define AMSYNCS_IOC_SET_PCR		_IOW((_A_M_SS), 0x0a, unsigned int)
-#define AMSYNCS_IOC_GET_PCR		_IOWR((_A_M_SS), 0x0b, unsigned int)
+#define AMSYNCS_IOC_SET_PCR		_IOW((_A_M_SS), 0x0a, struct pcr_pair)
+#define AMSYNCS_IOC_GET_PCR		_IOWR((_A_M_SS), 0x0b, struct pcr_pair)
 #define AMSYNCS_IOC_GET_WALL		_IOR((_A_M_SS), 0x0c, struct pts_wall)
 #define AMSYNCS_IOC_SET_RATE		_IOW((_A_M_SS), 0x0d, unsigned int)
 #define AMSYNCS_IOC_GET_RATE		_IOR((_A_M_SS), 0x0e, unsigned int)
@@ -115,4 +127,7 @@
 #define AMSYNCS_IOC_GET_CLOCK_START	_IOR((_A_M_SS), 0x12, unsigned int)
 #define AMSYNCS_IOC_AUDIO_START	_IOW((_A_M_SS), 0x13, struct audio_start)
 
+//For debuging
+#define AMSYNCS_IOC_GET_DEBUG_MODE      _IOR((_A_M_SS), 0x100, struct session_debug)
+
 #endif
diff --git a/src/msync_util.c b/src/msync_util.c
index 4eaa41d..7ead777 100644
--- a/src/msync_util.c
+++ b/src/msync_util.c
@@ -332,11 +332,13 @@
     return start != 0;
 }
 
-int msync_session_set_pcr(int fd, pts90K pts)
+int msync_session_set_pcr(int fd, pts90K pts, uint64_t mono_clock)
 {
     int rc;
-    uint32_t pcr = pts;
+    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);
@@ -344,16 +346,29 @@
     return rc;
 }
 
-int msync_session_get_pcr(int fd, pts90K *pts)
+int msync_session_get_pcr(int fd, pts90K *pts, uint64_t *mono_clock)
 {
     int rc;
-    uint32_t pcr;
+    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;
+    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;
 }
diff --git a/src/msync_util.h b/src/msync_util.h
index 89be112..67dbfb3 100644
--- a/src/msync_util.h
+++ b/src/msync_util.h
@@ -16,6 +16,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include "aml_avsync.h"
+#include "msync.h"
 
 int msync_create_session();
 void msync_destory_session(int id);
@@ -41,7 +42,8 @@
 int msync_session_get_stat (int fd, enum sync_mode *mode,
         bool *v_active, bool *a_active, bool *v_timeout);
 bool msync_clock_started(int fd);
-int msync_session_set_pcr(int fd, pts90K pts);
-int msync_session_get_pcr(int fd, pts90K *pts);
+int msync_session_set_pcr(int fd, pts90K pts, uint64_t mono_clock);
+int msync_session_get_pcr(int fd, pts90K *pts, uint64_t *mono_clock);
+int msync_session_get_debug_mode(int fd, struct session_debug *debug);
 
 #endif