Disable Weston video playback offset

現在目標與狀況

    目標

    • Disable Weston video playback
    • User hardware decode

    遇到狀況

    • Enable Weston video playback normally
    • gst-launch-1.0 filesrc location=/home/root/demo.mp4 ! \
      qtdemux name=d d.video_0 ! queue ! h264parse ! vpudec ! \
      queue ! waylandsink
    • Disable Weston video playback offset
    • gst-launch-1.0 -v \
      filesrc location=/home/root/demo.mp4 ! \
      qtdemux name=d d.video_0 ! queue ! h264parse ! \
      vpudec ! glimagesink

驗證分析

  • 同一支影片、同一個 decoder,在不同video sink,行為不同
  • 更換video sink
    • glimagesink: 是 OpenGL 視窗/GL render 路徑
    • kmssink: 是直接走 DRM/KMS plane 的路徑
  • 如果要避開 Weston,又想低 CPU,應該要是decoder → KMS plane
  • 簡單測試kmssink
  • gst-launch-1.0 -v videotestsrc ! kmssink
            
    # 發現顯示 Caught SIGSEGV,所以kmssink有bug
  • 用 modetest 去看DRM支援什麼
  • modetest -M imx-drm
        
    # formats: XR24 AR24 RG16 XB24 AB24 RX24 RA24 AR15 XR15 AB15 XB15 BG16
    # 沒有 NV12 / NV16
    # vpudec 解碼輸出影像格式是 NV12,DRM plane不支援顯示 NV12
  • 不使用vpudec,指定輸出格式丟到kmssink
  • videotestsrc ! videoconvert ! video/x-raw,format=BGRx,width=1280,height=800 ! kmssink
        
    # 還是crash
  • 使用gdb debug
  • gdb --args gst-launch-1.0 --gst-disable-segtrap -v \
    videotestsrc ! videoconvert ! \
    video/x-raw,format=BGRx,width=1280,height=800 ! \
    kmssink driver-name=imx-drm plane-id=29 
    
    # --args : 後續為GStreamer指令
    # --gst-disable-segtrap : GStreamer 不攔截 SIGSEGV

    (gdb) handle SIGSEGV stop print nopass
        
    # stop:當GStreamer收到 SIGSEGV 時停止執行
    # print:收到 SIGSEGV 時 GDB 會打印信號信息
    # nopass:GStreamer不會自行處理或崩潰,由 GDB 接管

    (gdb) run
    # 正式啟動GStreamer
    #其中發現
    Thread 2 "videotestsrc0:s" received signal SIGSEGV, Segmentation fault.
    [Switching to Thread 0xffffbeed91f0 (LWP 4051)]
    strcmp () at ../sysdeps/aarch64/strcmp.S:64
    
    (gdb) bt
    #0  strcmp () at ../sysdeps/aarch64/strcmp.S:64
    #1  0x0000ffffbef8117c in ?? () from /usr/lib/gstreamer-1.0/libgstkms.so
    #2  0x0000000000652df0 in ?? ()
    #3  0x657361625f747367 in ?? ()
    
    # 表示libgstkms.so 裡某個函式呼叫 strcmp() 時出了問題
    

    (gdb) thread apply all bt
    # 檢視其他thread是否有問題
    # 顯示 :
    	Thread 2
    		停在strcmp() → 這就是出事的 thread
    	Thread 1 / Thread 3
        	在等待
    

  • 確認是strcmp()問題,回到gstreamer1.0-plugins source找尋strcmp()
  • grep -R "strcmp *(" -n sys/kms
    sys/kms/gstkmssink.c:496:  if (strcmp (get_imx_drm_device_name(), "DPU") == 0) {
    sys/kms/gstkmssink.c:747:    if (!strcmp (property->name, prop_name)) {
    sys/kms/gstkmssink.c:1446:      if (!strcmp(prop->name, "dtrc_table_ofs")) {
    sys/kms/gstkmssink.c:1670:    if (!strcmp(prop->name, "dtrc_table_ofs") && dtrc_table_ofs) {
    sys/kms/gstkmssink.c:1696:    if (!strcmp(prop->name, "alpha")) {
    sys/kms/gstkmssink.c:1948:        if (!strcmp(prop->name, "HDR_SOURCE_METADATA")) {
    sys/kms/gstkmssink.c:2020:  if (strcmp (get_imx_drm_device_name(), "DPU") == 0) {
    

  • 確認是strcmp()問題,回到gstreamer1.0-plugins source找尋strcmp()
  • grep -R "strcmp *(" -n sys/kms
    sys/kms/gstkmssink.c:496:  if (strcmp (get_imx_drm_device_name(), "DPU") == 0) {
    sys/kms/gstkmssink.c:747:    if (!strcmp (property->name, prop_name)) {
    sys/kms/gstkmssink.c:1446:      if (!strcmp(prop->name, "dtrc_table_ofs")) {
    sys/kms/gstkmssink.c:1670:    if (!strcmp(prop->name, "dtrc_table_ofs") && dtrc_table_ofs) {
    sys/kms/gstkmssink.c:1696:    if (!strcmp(prop->name, "alpha")) {
    sys/kms/gstkmssink.c:1948:        if (!strcmp(prop->name, "HDR_SOURCE_METADATA")) {
    sys/kms/gstkmssink.c:2020:  if (strcmp (get_imx_drm_device_name(), "DPU") == 0) {
    

    (gdb) info registers
    # 顯示目前 CPU register的內容
    
    x0 0xdf8475800 60000000000
    x1 0xffffbef86390 281473885692816 
    x2 0xffffb8000080 281473768751232 
    x3 0xffffb80008d0 281473768753360
    ...
    ...
    ...
    
    

    (gdb) x/16i $pc
    # 反組譯(disassemble)目前程式執行machine Code
    
    0xffffbf326d18 <strcmp+24>: ldr x2, [x0], #8 
    0xffffbf326d1c <strcmp+28>: ldr x3, [x1], #8
    ...
    ...
    ...
    
    

  • 依造上述 (gdb) info registers 和 (gdb) (gdb) x/16i $pc 的內容
  • strcmp(a, b);
    x0 = a
    x1 = b
    call strcmp
    
    所以實際上執行strcmp(x0, x1)
    x0 = 0xdf8475800   (60000000000) 看起來很不像合法字串位址
    
  • 所以結合source找尋strcmp() 的結果,像問題點只有
  • sys/kms/gstkmssink.c:496:  if (strcmp (get_imx_drm_device_name(), "DPU") == 0) {
    sys/kms/gstkmssink.c:2020:  if (strcmp (get_imx_drm_device_name(), "DPU") == 0) {
  • 查 get_imx_drm_device_name() 的實作
  • get_imx_drm_device_name (void){
    const gchar * device;
    ...
    if (strstr (entry->d_name, "dpu@")) {
      device = "DPU";
      break;
    }
    if (strstr (entry->d_name, "dcss@")) {
      device = "DCSS";
      break;
    }
    ...
    return device;
    }
  • i.MX 8M Mini Applications Processor Reference Manual
    • 1.4.11 Display Interfaces
        The chip has the following display support:
          • LCDIF Display Controller:
          • Supports one layer
          • Supports up to 1920x1200p60 display through MIPI DSI
          • MIPI Interface:
          • 4-lane MIPI CSI interface
          • 4-lane MIPI DSI interface
          • CSI Interface:
          • CSI is a simple camera interface which is used to capture the MIPI CSI input and
        save the pixels into memory

解決方式

    1. 修正初始化
    2. const gchar * device = NULL;
          
      找得到 dpu@ → 回 "DPU"
      找得到 dcss@ → 回 "DCSS"
      找不到 → 回 NULL
      
      讓程式變成一個可被程式判斷和處理狀態
      

    3. 補上LCDIF
    4. if (strstr (entry->d_name, "dpu@")) {
      	device = "DPU";
      	break;
      }
      
      if (strstr (entry->d_name, "dcss@")) {
      	device = "DCSS";
      	break;
      }
          
      if (strstr (entry->d_name, "lcdif@")) {
      	device = "LCDIF";
      	break;
      }

    5. 避免 crash
    6. g_strcmp0(get_imx_drm_device_name(), "DPU") == 0
      
      g_strcmp0(a, b) 和 strcmp(a, b) 類似,但差別在:
      它允許 a 或 b 是NULL
      不會因為NULL直接crash只會回傳「不相等」

留言