avsync-lib: support underflow detection [3/3]

PD#TV-45115

Problem:
New feature requirment for vsink

Solution:
added underflow detection call back

Verify:
T5D + Roxton + NTS

Change-Id: I9fa3fe9e6b5a942221415b03fd29aff9d02236fa
Signed-off-by: yongchun.li <yongchun.li@amlogic.com>
diff --git a/src/aml_avsync.h b/src/aml_avsync.h
index c8eacea..2743768 100644
--- a/src/aml_avsync.h
+++ b/src/aml_avsync.h
@@ -60,6 +60,7 @@
 struct vframe;
 typedef void (*free_frame)(struct vframe * frame);
 typedef void (*pause_pts_done)(uint32_t pts, void* priv);
+typedef void (*underflow_detected)(uint32_t pts, void* priv);
 
 typedef enum {
     /* good to render */
@@ -135,6 +136,9 @@
     /*timeout in ms */
     int timeout;
 };
+struct underflow_config {
+    int time_thresh; /* underflow check time threshold in ms */
+};
 
 /* Open a new session and create the ID
  * Params:
@@ -423,4 +427,16 @@
  *   CLK_RECOVERY_ERR: error happens
  */
 enum  clock_recovery_stat av_sync_get_clock_deviation(void *sync, int32_t *ppm);
+
+/* set underflow detect call back
+ * av sync will callback when a buffer underflow detected when normal play
+ * Params:
+ *   @sync: AV sync module handle
+ *   @cb: callback function
+ *   @priv: callback function parameter
+ *   @cfg: the configuration of the call back, NULL use default value.
+ * Return:
+ *   0 for OK, or error code
+ */
+int av_sync_set_underflow_check_cb(void *sync, underflow_detected cb, void *priv, struct underflow_config *cfg);
 #endif
diff --git a/src/avsync.c b/src/avsync.c
index 8ee61b9..b265f9b 100644
--- a/src/avsync.c
+++ b/src/avsync.c
@@ -104,6 +104,11 @@
     pts90K pause_pts;
     pause_pts_done pause_pts_cb;
     void *pause_cb_priv;
+    /* underflow */
+    underflow_detected underflow_cb;
+    void *underflow_cb_priv;
+    struct underflow_config underflow_cfg;
+    struct timespec frame_last_update_time;
 
     /* log control */
     uint32_t last_log_syst;
@@ -163,6 +168,7 @@
 #define STREAM_DISC_THRES (TIME_UNIT90K / 10)
 #define OUTLIER_MAX_CNT 8
 #define VALID_TS(x) ((x) != -1)
+#define UNDERFLOW_CHECK_THRESH_MS (100)
 
 static uint64_t time_diff (struct timespec *b, struct timespec *a);
 static bool frame_expire(struct av_sync_session* avsync,
@@ -535,7 +541,10 @@
     avsync->paused = pause;
     log_info("[%d]paused:%d type:%d rc %d",
         avsync->session_id, pause, avsync->type, rc);
-
+    if (!avsync->paused && avsync->first_frame_toggled) {
+        clock_gettime(CLOCK_MONOTONIC_RAW, &avsync->frame_last_update_time);
+        log_info("[%d] resume update new frame time", avsync->session_id);
+    }
     return rc;
 }
 
@@ -717,6 +726,7 @@
             }
             avsync->last_frame = frame;
             avsync->last_pts = frame->pts;
+            clock_gettime(CLOCK_MONOTONIC_RAW, &avsync->frame_last_update_time);
         } else
             break;
     }
@@ -746,6 +756,26 @@
 
 exit:
     pthread_mutex_unlock(&avsync->lock);
+
+    /* underflow check */
+    if (avsync->session_started && avsync->first_frame_toggled &&
+        (avsync->paused == false) && (avsync->state >= AV_SYNC_STAT_RUNNING) &&
+        avsync->underflow_cb && peek_item(avsync->frame_q, (void **)&frame, 0))
+    {/* empty queue in normal play */
+        struct timespec now;
+        int diff_ms;
+        clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+        diff_ms = time_diff(&now, &avsync->frame_last_update_time)/1000;
+        if(diff_ms >= (avsync->underflow_cfg.time_thresh
+                       + avsync->vsync_interval*avsync->last_holding_peroid/90)) {
+            log_info ("[%d]underflow detected: %u", avsync->session_id, avsync->last_pts);
+            avsync->underflow_cb (avsync->last_pts,
+                    avsync->underflow_cb_priv);
+            /* update time to control the underflow check call backs */
+            avsync->frame_last_update_time = now;
+        }
+    }
+
     if (avsync->last_frame) {
         if (enter_last_frame != avsync->last_frame)
             log_debug("[%d]pop %u", avsync->session_id, avsync->last_frame->pts);
@@ -1083,6 +1113,26 @@
     return 0;
 }
 
+int av_sync_set_underflow_check_cb(void *sync, underflow_detected cb, void *priv, struct underflow_config *cfg)
+{
+    struct av_sync_session *avsync = (struct av_sync_session *)sync;
+
+    if (!avsync)
+      return -1;
+
+    avsync->underflow_cb = cb;
+    avsync->underflow_cb_priv = priv;
+
+    if (cfg)
+        avsync->underflow_cfg.time_thresh = cfg->time_thresh;
+    else
+        avsync->underflow_cfg.time_thresh = UNDERFLOW_CHECK_THRESH_MS;
+
+    log_info("[%d] av_sync_set_underflow_check_cb %p priv %p time %d",
+             avsync->session_id, avsync->underflow_cb, avsync->underflow_cb_priv,
+             avsync->underflow_cfg.time_thresh);
+    return 0;
+}
 static void trigger_audio_start_cb(struct av_sync_session *avsync,
         avs_ascb_reason reason)
 {