v4l2-uvm-test: integrate with avsync-lib [2/2]

PD#SWPL-28037

Problem:
Missing AV sync function

Solution:
Use avync-lib for video AV sync.
When PTS is not in contrainer, use duration to fake PTS

Verify:
U212 + v4l2-uvm-test

Change-Id: I1f01ea58d151efbdc5a4b9cb04873f1f153a11a1
diff --git a/v4l2-uvm-test/Config.in b/v4l2-uvm-test/Config.in
index d6771a9..1cc424b 100755
--- a/v4l2-uvm-test/Config.in
+++ b/v4l2-uvm-test/Config.in
@@ -2,6 +2,7 @@
         bool "v4l2_UVM_test"
         select BR2_PACKAGE_LIBDRM
         select BR2_PACKAGE_FFMPEG
+        select BR2_PACKAGE_AV_SYNC_LIB
         help
           Test application for v4l2 decoder using DRM-GEM UVM buffer.
           http://openlinux.amlogic.com
diff --git a/v4l2-uvm-test/src/Makefile b/v4l2-uvm-test/src/Makefile
index f35c5e9..91de0ba 100644
--- a/v4l2-uvm-test/src/Makefile
+++ b/v4l2-uvm-test/src/Makefile
@@ -8,7 +8,7 @@
 all: $(TARGET)
 
 #TARGET_CFLAGS += -DDEBUG_FRAME
-LD_FLAG = -ldrm -lavformat -lavcodec -lavutil -lm -lpthread -lz -lswresample
+LD_FLAG = -ldrm -lavformat -lavcodec -lavutil -lm -lpthread -lz -lswresample -lamlavsync
 
 ifeq ($(SECMEM), 1)
 OBJ += secmem.c
@@ -18,7 +18,7 @@
 
 
 $(TARGET): $(OBJ)
-	$(CC) $(TARGET_CFLAGS)  -D_FILE_OFFSET_BITS=64 -Wall -I$(STAGING_DIR)/usr/include/ -I$(STAGING_DIR)/usr/include/libdrm/ -L$(STAGING_DIR)/usr/lib $(LD_FLAG) $(OBJ) -o $@
+	$(CC) $(TARGET_CFLAGS)  -g -D_FILE_OFFSET_BITS=64 -Wall -I$(STAGING_DIR)/usr/include/ -I$(STAGING_DIR)/usr/include/libdrm/ -L$(STAGING_DIR)/usr/lib $(LD_FLAG) $(OBJ) -o $@
 
 .PHONY: clean
 
diff --git a/v4l2-uvm-test/src/demux.c b/v4l2-uvm-test/src/demux.c
index 9331ef4..5e1de97 100644
--- a/v4l2-uvm-test/src/demux.c
+++ b/v4l2-uvm-test/src/demux.c
@@ -19,7 +19,7 @@
 #define HEVC_NAL_SPS 33
 #define HEVC_NAL_PPS 34
 
-extern int ffmpeg_log;
+extern int log_level;
 /* ffmpeg data structure */
 static AVStream *video_stream = NULL;
 static AVFormatContext *fmt_ctx = NULL;
@@ -37,6 +37,7 @@
 static int thread_quit;
 static struct dmx_v_data v_data;
 static int video_frame_count = 0;
+static int64_t fake_pts = 0;
 
 static void dump(const char* path, uint8_t *data, int size) {
     FILE* fd = fopen(path, "wb");
@@ -82,13 +83,18 @@
     if (pkt->stream_index != video_stream_idx)
         return decoded;
 
-    if (pkt->pts != AV_NOPTS_VALUE)
+    if (pkt->pts != AV_NOPTS_VALUE) {
         pts_us = av_rescale_q(pkt->pts, video_stream->time_base, ns_r);
+        fake_pts = pts_us;
+    } else {
+        pts_us = fake_pts + av_rescale_q(pkt->duration, video_stream->time_base, ns_r);
+        fake_pts = pts_us;
+    }
 
     /* video frame */
     video_frame_count++;
 #ifdef DEBUG_FRAME
-    printf("video_frame n:%d pts:%llx size:%x\n",
+    printf("video_frame n:%d pts:%lld size:%x\n",
             video_frame_count,
             pts_us, pkt->size);
 #endif
@@ -393,7 +399,7 @@
 static void log_callback(void *ptr, int level,
         const char *fmt, va_list vargs)
 {
-    if (ffmpeg_log)
+    if (log_level & 0x1)
         vprintf(fmt, vargs);
 }
 
diff --git a/v4l2-uvm-test/src/drm.c b/v4l2-uvm-test/src/drm.c
index d5e8637..9938ee4 100644
--- a/v4l2-uvm-test/src/drm.c
+++ b/v4l2-uvm-test/src/drm.c
@@ -28,6 +28,8 @@
 #include <linux/videodev2.h>
 #include <meson_drm.h>
 
+#include "aml_avsync.h"
+#include "aml_avsync_log.h"
 #include "drm.h"
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 #define MAX_BUF_SIZE 32
@@ -40,6 +42,7 @@
 extern unsigned int global_plane_id;
 extern char mode_str[16];
 extern unsigned int vfresh;
+extern int log_level;
 
 struct gem_buffer {
     uint32_t width;
@@ -69,7 +72,6 @@
     uint32_t plane_crtc_w;
     uint32_t plane_crtc_h;
     uint32_t plane_zpos;
-    uint32_t out_fence_ptr;
 };
 
 struct plane {
@@ -114,11 +116,15 @@
 struct aml_display {
     bool started;
     pthread_t disp_t;
+#if 0
     unsigned int q_len;
     unsigned int ri; //read index
     unsigned int wi; //write index
     unsigned int total_num;
     struct drm_frame *queue;
+#endif
+    void * avsync;
+    bool last_frame;
 };
 
 static struct gem_buffer *gem_buf;
@@ -126,6 +132,10 @@
 static displayed_cb_func display_cb;
 static struct aml_display aml_dis;
 
+#define TSYNC_MODE   "/sys/class/tsync/mode"
+#define TSYNC_PCRSCR "/sys/class/tsync/pts_pcrscr"
+#define VPTS_INC_UPINT "/sys/class/video/vsync_pts_inc_upint"
+
 static int create_meson_gem_buffer(int fd, enum frame_format fmt,
         struct gem_buffer *buffer)
 {
@@ -264,7 +274,6 @@
 static int discover_properties(int fd, struct display_properties_ids *ids)
 {
     //int connector_id = setup.connector_id;
-    int crtc_id = setup.crtc_id;
     int plane_id = setup.plane_id;
     drmModeObjectPropertiesPtr properties = NULL;
     drmModePropertyPtr property = NULL;
@@ -284,7 +293,6 @@
         { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_Y", &ids->plane_crtc_y },
         { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_W", &ids->plane_crtc_w },
         { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_H", &ids->plane_crtc_h },
-        { DRM_MODE_OBJECT_CRTC, crtc_id, "OUT_FENCE_PTR", &ids->out_fence_ptr},
     };
     unsigned int i, j;
     int rc;
@@ -406,7 +414,7 @@
     drmModeAtomicReqPtr request;
     uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
     int rc;
-    int out_fence_fd = 0;
+    //int out_fence_fd = 0;
     //struct timeval t1, t2;
 
     request = drmModeAtomicAlloc();
@@ -450,7 +458,6 @@
             gem_buf->framebuffer_id);
     drmModeAtomicAddProperty(request, plane_id, ids->plane_crtc_id,
             crtc_id);
-    drmModeAtomicAddProperty(request, crtc_id, ids->out_fence_ptr, (unsigned long)&out_fence_fd);
 
     rc = drmModeAtomicCommit(fd, request, flags, NULL);
     if (rc < 0) {
@@ -458,15 +465,6 @@
         goto error;
     }
 
-    //gettimeofday(&t1, NULL);
-    rc = sync_wait(out_fence_fd, 50);
-    if (rc < 0)
-        printf("wait out fence fail %d, %s\n", rc, strerror(errno));
-    else {
-        //gettimeofday(&t2, NULL);
-        //printf("%s %d span:%d\n", __func__, __LINE__, span(&t1, &t2));
-    }
-    close(out_fence_fd);
     rc = 0;
     goto complete;
 
@@ -683,8 +681,9 @@
         printf("fail to open %s\n", path);
         return -1;
     }
-    if (write(fd, value, strlen(value)) != 1) {
+    if (write(fd, value, strlen(value)) != strlen(value)) {
         printf("fail to write %s to %s\n", value, path);
+        close(fd);
         return -1;
     }
     close(fd);
@@ -695,8 +694,8 @@
 int display_engine_start(int smode)
 {
     unsigned int plane_id;
-    drmModeModeInfo mode;
     int rc;
+    drmModeModeInfo mode;
 
     secure_mode = smode;
 
@@ -811,6 +810,18 @@
         printf("drmModeSetCrtc fail %d\n", rc);
         return -1;
     }
+
+    /* video master mode for testing */
+    config_sys_node(TSYNC_MODE, "0");
+    config_sys_node(TSYNC_PCRSCR, "0");
+    config_sys_node(VPTS_INC_UPINT, "1");
+
+    log_set_level((log_level >> 1) & 0x7);
+    aml_dis.avsync = av_sync_create(0, 2, 2, 90000/mode.vrefresh);
+    if (!aml_dis.avsync) {
+        printf("create avsync fails\n");
+        return -1;
+    }
     return 0;
 }
 
@@ -890,9 +901,13 @@
     aml_dis.started = false;
     pthread_join(aml_dis.disp_t, NULL);
     close_buffer(drm_cli_fd, &osd_gem_buf);
+    if (aml_dis.avsync)
+        av_sync_destroy(aml_dis.avsync);
 
+#if 0
     if (aml_dis.queue)
         free(aml_dis.queue);
+#endif
     if (gem_buf)
         free(gem_buf);
     if (setup.plane) {
@@ -918,6 +933,7 @@
     return 0;
 }
 
+#if 0
 static int queue_frame(struct drm_frame* frame)
 {
     if (aml_dis.total_num == aml_dis.q_len)
@@ -945,50 +961,82 @@
 
     return 0;
 }
+#endif
 
 static void * display_thread_func(void * arg)
 {
-    struct drm_frame f, f_p1, f_p2;
-    unsigned int fence_num;
+    struct vframe *sync_frame;
+    struct drm_frame *f = NULL, *f_p1 = NULL, *f_p2 = NULL;
+    drmVBlank vbl;
 
-    f.pri_dec = NULL;
-    f_p1.pri_dec = NULL;
-    f_p2.pri_dec = NULL;
+    memset(&vbl, 0, sizeof(drmVBlank));
 
     while (aml_dis.started) {
         int rc;
         struct gem_buffer* gem_buf;
 
-        rc = dequeue_frame(&f);
-        if (rc) {
-            usleep(10);
-            continue;
-        }
-        gem_buf = f.gem;
+        vbl.request.type = DRM_VBLANK_RELATIVE;
+        vbl.request.sequence = 1;
+        vbl.request.signal = 0;
 
-        rc = page_flip(drm_fd, setup.crtc_id, setup.plane_id,
-                &setup.properties_ids, gem_buf);
+        rc = drmWaitVBlank(drm_fd, &vbl);
         if (rc) {
-            printf("page_flip error\n");
-            continue;
-        }
-        fence_num++;
-        /* 2 fence delay on video layer, 1 fence delay on osd */
-        if (f_p2.pri_dec) {
-            display_cb(f_p2.pri_dec);
+            printf("drmWaitVBlank error %d\n", rc);
+            return NULL;
         }
 
-        f_p2 = f_p1;
-        f_p1 = f;
+        sync_frame = av_sync_pop_frame(aml_dis.avsync);
+        if (!sync_frame)
+            continue;
+
+        f = sync_frame->private;
+
+        if (!f) {
+            aml_dis.last_frame = true;
+            break;
+        }
+
+        log_debug("pop frame: %d", f->pts);
+        if (f != f_p1) {
+            gem_buf = f->gem;
+            rc = page_flip(drm_fd, setup.crtc_id, setup.plane_id,
+                    &setup.properties_ids, gem_buf);
+            if (rc) {
+                printf("page_flip error\n");
+                continue;
+            }
+            /* 2 fence delay on video layer, 1 fence delay on osd */
+            if (f_p2) {
+                display_cb(f_p2->pri_dec);
+                free(f_p2->pri_sync);
+            }
+
+            f_p2 = f_p1;
+            f_p1 = f;
+        }
     }
+    printf("quit %s\n", __func__);
     return NULL;
 }
 
+static void sync_frame_free(struct vframe * sync_frame)
+{
+    struct drm_frame* drm_f = sync_frame->private;
+
+    if (drm_f) {
+        display_cb(drm_f->pri_dec);
+        free(sync_frame);
+    } else
+        aml_dis.last_frame = true;
+}
+
 int display_engine_show(struct drm_frame* frame)
 {
     int rc;
+    struct vframe* sync_frame;
 
     if (!aml_dis.started) {
+#if 0
         aml_dis.queue = (struct drm_frame*)calloc(MAX_BUF_SIZE, sizeof(*aml_dis.queue));
         if (!aml_dis.queue) {
             printf("%s OOM\n", __func__);
@@ -996,7 +1044,9 @@
         }
         aml_dis.q_len = MAX_BUF_SIZE;
         aml_dis.ri = aml_dis.wi = aml_dis.total_num = 0;
+#endif
         aml_dis.started = true;
+        aml_dis.last_frame = false;
 
         rc = pthread_create(&aml_dis.disp_t, NULL, display_thread_func, NULL);
         if (rc) {
@@ -1005,13 +1055,27 @@
         }
     }
 
+    sync_frame = calloc(1, sizeof(*sync_frame));
+    if (!sync_frame) {
+        printf("%s OOM\n", __func__);
+        return -1;
+    }
+    if (!frame->last_flag) {
+        sync_frame->private = frame;
+        sync_frame->pts = frame->pts;
+        frame->pri_sync = sync_frame;
+        //TODO: fill correct duration
+    }
+    sync_frame->duration = 0;
+    sync_frame->free = sync_frame_free;
     while (aml_dis.started) {
-        if (queue_frame(frame)) {
-            usleep(10);
+        if (av_sync_push_frame(aml_dis.avsync, sync_frame)) {
+            usleep(1000);
             continue;
         } else
             break;
     }
+    //printf("push frame: %d\n", sync_frame->pts);
 
     return 0;
 }
@@ -1024,7 +1088,7 @@
 
 int display_wait_for_display()
 {
-    while (aml_dis.started && aml_dis.total_num) {
+    while (aml_dis.started && !aml_dis.last_frame) {
         usleep(10);
     }
     if (!aml_dis.started)
diff --git a/v4l2-uvm-test/src/drm.h b/v4l2-uvm-test/src/drm.h
index fe74354..3daa4b4 100644
--- a/v4l2-uvm-test/src/drm.h
+++ b/v4l2-uvm-test/src/drm.h
@@ -25,6 +25,9 @@
     void* gem;
     void* pri_dec;
     drm_frame_destroy destroy;
+    uint32_t pts;
+    bool last_flag;
+    void* pri_sync;
 };
 
 typedef int (*displayed_cb_func)(void* handle);
diff --git a/v4l2-uvm-test/src/test.c b/v4l2-uvm-test/src/test.c
index 7510c0b..9d93760 100644
--- a/v4l2-uvm-test/src/test.c
+++ b/v4l2-uvm-test/src/test.c
@@ -30,7 +30,7 @@
 unsigned int vfresh;
 int g_dw_mode = 16;
 static int secure_mode = 0;
-int ffmpeg_log = 0;
+int log_level = 0x04;
 
 static void usage(int argc, char **argv)
 {
@@ -48,13 +48,13 @@
                  "-p | --plane=id      select display plane. 26[pri] 28[overlay 1] 30[overlay 2] 32[video]\n"
                  "-m | --mode str      set display mode. such as 3840x2160 or 3840x2160-60\n"
                  "                                               1920x1080 or 1920x1080-60\n"
-                 "-l | --log           enable more ffmpeg demux log.\n"
+                 "-l | --log           enable more log bit 0: ffmpeg demux bit 1/2/3: avsync.\n"
                  "-s | --secure        secure video path.\n"
                  "",
                  argv[0]);
 }
 
-static const char short_options[] = "d:hf:p:m:ls";
+static const char short_options[] = "d:hf:p:m:l:s";
 
 static const struct option
 long_options[] = {
@@ -63,7 +63,7 @@
         { "help",   no_argument,       NULL, 'h' },
         { "plane",  required_argument, NULL, 'p' },
         { "mode", required_argument, NULL, 'm' },
-        { "log",  no_argument, NULL, 'l' },
+        { "log",  required_argument, NULL, 'l' },
         { "secure",  no_argument, NULL, 's' },
         { 0, 0, 0, 0 }
 };
@@ -103,7 +103,7 @@
                 break;
 
             case 'l':
-                ffmpeg_log = 1;
+                log_level = atoi(optarg);
                 break;
 
             case 'd':
diff --git a/v4l2-uvm-test/src/v4l2-dec.c b/v4l2-uvm-test/src/v4l2-dec.c
index a157fec..4917a8f 100644
--- a/v4l2-uvm-test/src/v4l2-dec.c
+++ b/v4l2-uvm-test/src/v4l2-dec.c
@@ -21,6 +21,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <libavformat/avformat.h>
 
 #include "aml_driver.h"
 #include "demux.h"
@@ -547,6 +548,10 @@
                 }
                 if (!(buf.flags & V4L2_BUF_FLAG_LAST)) {
                     struct drm_frame *drm_f = capture_p.buf[buf.index]->drm_frame;
+                    uint64_t pts_us;
+
+                    AVRational us_r = {1,DMX_SECOND};
+                    AVRational ninty_k_r = {1,90000};
                     /* display capture buf */
                     if (res_profile.res_change) {
                         res_profile.res_change = false;
@@ -557,6 +562,10 @@
                                 span(&res_profile.last_flag, &res_profile.first_frame));
                     }
                     drm_f->pri_dec =  capture_p.buf[buf.index];
+                    pts_us = buf.timestamp.tv_sec * DMX_SECOND;
+                    pts_us += buf.timestamp.tv_usec;
+                    drm_f->pts = av_rescale_q(pts_us, us_r, ninty_k_r);
+                    drm_f->last_flag = false;
                     display_engine_show(drm_f);
                 } else {
                     uint32_t evt = 0;
@@ -574,6 +583,12 @@
                         d_c_rec_num++;
                         continue;
                     } else if (evt == V4L2_EVENT_EOS || eos_evt_pending) {
+                        struct drm_frame *drm_f = capture_p.buf[buf.index]->drm_frame;
+
+                        /* flush DRM */
+                        drm_f->last_flag = true;
+                        display_engine_show(drm_f);
+
                         printf("EOS received\n");
                         eos_received = true;
                         eos_evt_pending = false;
diff --git a/v4l2-uvm-test/v4l2-uvm-test.mk b/v4l2-uvm-test/v4l2-uvm-test.mk
index 4c25926..6f0bf94 100644
--- a/v4l2-uvm-test/v4l2-uvm-test.mk
+++ b/v4l2-uvm-test/v4l2-uvm-test.mk
@@ -7,6 +7,7 @@
 
 V4L2_UVM_TEST_DEPENDENCIES += ffmpeg
 V4L2_UVM_TEST_DEPENDENCIES += libdrm
+V4L2_UVM_TEST_DEPENDENCIES += avsync-lib
 
 ifneq ($(BR2_PACKAGE_LIBSECMEM),y)
 V4L2_UVM_TEST_DEPENDENCIES  += libsecmem-bin