【Camera專題】-Camera幀率、黃光環境下拍照閃紅問題-【展訊平臺】

c楓_擼碼的日子發表於2019-01-21

吐槽

換了新公司,一上來就面對兩個比較棘手的問題,2個問題都是拖了幾個月沒有解決,跟展訊那邊溝通遲遲沒有解決方案。

原本是做MTK平臺的,到了這邊需要做展訊平臺和高通平臺。

證明能力的時候到了!

一週用來熟悉展訊Camera驅動,2個問題基本在加班跟原始碼,3天左右找到解決方案!

事實上還是有些成就感,畢竟,一來面對的是新平臺,另外特別是第二個問題,展訊拖了幾個月都沒有解決辦法。

問題1:視訊通話下,固定幀率為10幀,優化功耗,不影響拍照出幀數。

難點:

1.區分是視訊通話還是camera正常使用。

因為視訊通話和正常使用相機呼叫的都是camera preivew進行預覽。

  解決方案:
  1.在上層進行區分:
    聯絡微聊APP廠商在接聽通話時,往底層傳送一條msg(複雜,不可取)
複製程式碼
  2.在HAL層進行區分:
    視訊通話時camera預覽的寬高(480,360),拍照時camera預覽的寬高(480,480)
    void SprdCamera3OEMIf::setCameraPreviewMode(bool isRecordMode) {
	···
		if(height==360)根據高度進行區分
    	{
		    //設定一個標誌,表示正在視訊通話  
        	video_call_flag = true;
        }
	···
	}
		但是這個video_call_flag的值怎麼傳到底層,就有些複雜。
		方式一:
		新增persist_sys_video_call_flag節點,在底層去讀這個節點(無許可權,不可取)
		方式二:
    	從hal層通過指標傳參(已實現,改動複雜,不可取)
複製程式碼
	3.在底層進行區分(最優方案)
	通過log分析:由於視訊通話時,固定輸出幀率位10幀,
	因此,目標幀長總是dest_fre_len = dummy_line+shutter = 4326;
	這個值目標幀長時恆定的,我們可以採取這個點在底層進行區分。
	
複製程式碼

2.如何修改修改幀率。

Camera sensor知識點(型號:SP2609)

1.fps(幀率)計算方法:

    //cur_fr_len 表示當前一幀的長度
    if (cur_fr_len > shutter) {
        fps = 1000000.0 / (frame length* line_time);
    } else {
        fps = 1000000.0 / ((shutter + dummy_line) * line_time);
    }
複製程式碼
  • shutter 是寫入P1: 0x03,0x04暫存器
  • dummy_line:P1:0x05(高8位),0x06 (低8位)可以通過改變dummy_line來調整幀長->進而調整幀率
  • frame length(即cur_fr_len):可以通過P1:0x4e,0x4f讀出來(只能進行讀操作) 由於控制frame length的0x4e(高8位),0x4f(低8位)是隻讀的,無法寫入,那麼如何改變frame length呢?

2.frame length(幀長)的計算方式

if (frame length > shutter) {
  frame length = 1217+dummy_line,(初始值+dummy_line)
   //這裡的1217是當dummy_line的值為0時,可以用ISPTool工具去讀4e,4f的值得到的,也可以問一下FAE
  //舉個例子:如果dummy_line(0x05,0x0C),(0x06,0x25)->0C25->3109十進位制
 //那麼frame length = 1217 + 3109 = 4326
  
}else {
  frame length = shutter+-1;
}
複製程式碼
  • frame_length:表示一幀幀長,可以理解為一幀的行數;
  • shutter:曝光行數,單位是多少個line_time
  • dummy_line:用以改變幀長來調節幀率快慢,實際沒有資料輸出;
  • line_time:一行曝光時間(是固定值:23114)

幀長:限定最大fps,例如:

在初始化的時候,往dummy_line寫(0x05,0x0c),(0x06,0x25); 即忘05,06暫存器寫0c25->3109(十進位制) 則frame_length = 1217+dummy_line = 1217+3109;

那最大出幀率 max_fps = 1000000.0/(frame length* line_time)
                    = 1000000.0/(4326*23114)
  				  = 10 (fps)
複製程式碼

以上邏輯是做到sensor內部了,所以exposure介面函式只需要配置,平臺的AE Pline中exp變數轉化為exposure line count,然後寫入exp控制暫存器P1:0x03,0x04 以及P1:0x05,0x06即可,方法可以參考如下:

//shutter
static uint32_t sp2609_read_shutter(void)
{
   uint16_t shutter_h = 0;
   uint16_t shutter_l = 0;
       Sensor_WriteReg(0xfd, 0x01);//fd暫存器選擇第一頁
   shutter_h = Sensor_ReadReg(0x03) & 0xff;//寫入03暫存器(高8位)
   shutter_l = Sensor_ReadReg(0x04) & 0xff;//寫入04暫存器(低8位)
   return (shutter_h << 8) | shutter_l;//返回高8位和低8位拼接起來的值
}
static void sp2609_write_shutter(uint32_t shutter)
{		
   Sensor_WriteReg(0xfd, 0x01);
   Sensor_WriteReg(0x03, (shutter >> 8) & 0xff);
   Sensor_WriteReg(0x04, shutter & 0xff);
   Sensor_WriteReg(0x01, 0x01); //讓寫入的值立即生效   	
}
//dummy_line
static uint32_t sp2609_read_dummy_line(void)
{
   uint16_t dummy_line_h = 0;
   uint16_t dummy_line_l = 0;
       Sensor_WriteReg(0xfd, 0x01);//fd暫存器選擇第一頁
   shutter_h = Sensor_ReadReg(0x05) & 0xff;//寫入03暫存器(高8位)
   shutter_l = Sensor_ReadReg(0x06) & 0xff;//寫入04暫存器(低8位)
   return (dummy_line_h << 8) | dummy_line_l;//返回高8位和低8位拼接起來的值
}
static void sp2609_write_dummy_line(uint32_t dummy_line)
{		
   Sensor_WriteReg(0xfd, 0x01);
   Sensor_WriteReg(0x05, (dummy_line >> 8) & 0xff);
   Sensor_WriteReg(0x06, dummy_line & 0xff);
   Sensor_WriteReg(0x01, 0x01);    	
}
複製程式碼

3.解決方案:

vendor/sprd/modules/libcamera/sensor/sensor_drv/classic/Superpix/SP2609/sensor_sp2609_mipi_raw.c

//新增dummy_line介面
+static void sp2609_drv_write_dummy_line(cmr_handle handle, cmr_u32 dest_fr_len) {
    SENSOR_IC_CHECK_HANDLE_VOID(handle);
    struct sensor_ic_drv_cxt * sns_drv_cxt = (struct sensor_ic_drv_cxt *)handle;
	if(dest_fr_len == 4326) {
		//fix max fps = 10 in video call
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0xfd, 0x01);
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0x05, 0x0c);
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0x06, 0x25);
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0x01, 0x01);
	}else {
		//fix max fps = 30 in normol use camera
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0xfd, 0x01);
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0x05, 0x00);
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0x06, 0xe1);
		hw_sensor_write_reg(sns_drv_cxt->hw_handle, 0x01, 0x01);
	}
}
//在write_exposure_dummy時呼叫
cmr_int sp2609_drv_write_exposure_dummy(cmr_handle handle,···)
···
+    if (dest_fr_len != cur_fr_len) {
+write_sensor_dummy_line:
+		sp2609_drv_write_dummy_line(handle,dest_fr_len);
+	}
···
}
複製程式碼

視訊通話出幀率

【Camera專題】-Camera幀率、黃光環境下拍照閃紅問題-【展訊平臺】

問題2.黃色燈光或者夕陽西下環境下,拍照過程中瞬間閃紅問題。

1.現象

【Camera專題】-Camera幀率、黃光環境下拍照閃紅問題-【展訊平臺】

2.問題根源 這個現象和Camera的啟動時現象是一樣的。 camera啟動時,判斷當前是黃光環境(根據色溫),此時預覽介面偏紅,經過AWB(自動白平衡)演算法調整,就會變白一些。

那麼在拍照時,為啥會出現這個情況的?

【Camera專題】-Camera幀率、黃光環境下拍照閃紅問題-【展訊平臺】

也就是相機啟動或者是拍照切換資料流時,AE都會重新調整,導致出現這個問題。

3.解決方案

1.跳幀
  在拍照時,資料流切換的過程中,我們跳一幀來解決。
  在底層修改這個跳幀無效。
  
  在ISP端進行修改
  vendor/sprd/modules/libcamera/camdrv/isp2.3/driver/src/dcam_u_raw_aem.c
  cmr_s32 dcam_u_raw_aem_skip_num(cmr_handle handle, cmr_u32 skip_num)
  {
  ···
  + if(拍照時){
  + 	skip_num = 1
    }
  	param.property_param = &skip_num;
  ···
  }
  
複製程式碼

我們不能簡單粗暴的讓skip_num = 1,否則會造成相機啟動時,AWB調整會明顯感覺到慢一些,事實上dcam_u_raw_aem_skip_num函式在相機啟動的時候或者拍照的時候都會呼叫。 因此我們需要在拍照的時候跳一幀,啟動的時候還是不跳幀。

Stay hungry,Stay foolish!

相關文章