linux DVB 驅動分析


linux平臺的數字電視驅動被稱作linux DVB 驅動

其框架稱爲DVB core

在此做簡單介紹



Demux 簡介



Demux  是一個硬件模塊,的主要作用是處理從frontend過來的信號,從中分離出各種表,然後根據表中的信息分離出每個頻道的音頻信號和視頻信號,最後將音頻和視頻信號送到decoder解碼。





數字電視節目音頻視頻的ES流需要打包成PES(packetized elementary stream )包,一個PES包必須切爲N個TS包來傳送。每個TS  長188字節。

編碼器爲每一路的基本業務的傳送包賦予一個唯一的包標誌符PID ,
同時將這些PID的賦值信息寫入到節目映射表(PMT)的控制信息表中。每一路節目一個PMT表,記錄的是該路節目的視頻流的PID,音頻流的PID。

各個PMT的PID統一記錄在PAT表中。PAT的PID 爲0。

TS流解析流程

TS流中查找PID爲0的TS包,即PAT表
根據表獲取不同頻道的PMT表所使用的PID
根據這些PID獲取不同頻道的PMT
根據PMT表獲取頻道中的音視頻PID
根據音視頻PID 獲取音視頻TS包
獲取到TS包之後,TS包->PES包->ES包->解碼器。




linux DVB 驅動設備文件模型











DTV 播放,使用demux0
PVR Record , 使用demux1   DVR1
PVR Play, 使用demux0, DVR0


demux節點

/dev/dvb/adapter0/demux0
/dev/dvb/adapter0/demux1


1,   open: 可以多次打開。每次打開將會分配一個新的 filter  handle。
2,   read : 讀取sections,即PAT,PMT表
3,ioctl:  DMX_SET_PES_FILTER  
設置pes  過濾,即只讀取某pid的TS碼流。
設置TS流出口


typedef enum
{
DMX_OUT_DECODER,
DMX_OUT_TAP,
DMX_OUT_TS_TAP
} dmx_output_t;

DMX_OUT_DECODER:將流輸出到解碼器上
DMX_OUT_TAP:將流輸出到demux設備上   
  /dev/dvb/adapter0/demux0
  DMX_OUT_TS_TAP:將流輸出到邏輯 DVR設備上   /dev/dvb/adapter0/dvr0
  

  

DVR 節點

dev/dvb/adapter0/dvr0

dev/dvb/adapter0/dvr1

是一個邏輯設備。

從該設備讀取,將得到一個TS流。
該流就是在demux設備上設置了PES  PID過濾、設置了DMX_OUT_TS_TAP標誌之後,從demux 過濾出來的TS。

index節點
dev/dvb/adapter0/dvr_idx1
dev/dvb/adapter0/dvr_idx0


用來讀寫index流。
typedef struct
{
	driver_picture_type_e	picture_type;
	unsigned long long		packet_count;
	unsigned int		timecode;
	unsigned long long		pts;
}pvr_index_rawdata_s;

Index是解碼器生成的,並非TS流中自帶原始數據。
每個單元長32字節,主要描述了某一幀數據的I\B\P類型,在TS流中的位置、PTS時間戳。


PVR錄製典型例程



1,鎖住tuner頻率  (另文討論,不做贅述)
2,打開dvr設備/index設備
dvb_dvr_open
dvb_dvr_idx_open
3,demux設置:
設置PES過濾所需PID,以及TS流輸出方向
4,    設置trustzone加解密
5,進入循環,從dvr讀,向目標文件寫
While()
{
Read dvr
Read index
Write file
}


採用如下代碼打開DVR設備結點

	flags = O_RDONLY | O_NONBLOCK;
	sprintf(filename, "/dev/dvb/adapter%i/dvr%i", adapter, dvr);
	fd = open(filename, flags);


採用如下代碼打開index設備結點

	int flags = O_RDONLY | O_NONBLOCK;
	sprintf(filename, "/dev/dvb/adapter%i/dvr_idx%i", adapter, dvr_idx);
	fd = open(filename, flags);

採用如下代碼設置index設備結點
	dvr_index_config_t  index_config; 	
	index_config.video_format = DVR_VIDEO_FAMAT_MPEG;  //根據視頻流的格式設置
	index_config.interval =  0;  //通常情況下設置0
	ioctl(idxFd, DVR_IDX_SET_CONFIG, &index_config ) ;	

採用如下代碼打開demux設備結點
asprintf(&demux_name, "/dev/dvb/adapter%i/demux%i", adapter, demux );
  int fd_demux = open( demux_name, O_RDWR | O_NONBLOCK );
 
  採用如下代碼設置demux設備結點

對於每個需要得到的pid, 下面代碼均需要調用一次。
有幾個pid就調用幾次,同時要將pesfilter.pes_type 字段設置成pid對應的類型。
例如一個電視節目有audio,video,pcr, 三個pid數據流,則下面代碼就應該被調用三次,同時
pesfilter.pes_type分別設置成DMX_PES_AUDIO3, DMX_PES_VIDEO3, DMX_PES_PCR3

	struct dmx_pes_filter_params pesfilter;
	pesfilter.pid = pid;
	pesfilter.input = DMX_IN_FRONTEND;    //表示數據來自於前段電纜信號線
	pesfilter.output = DVB_DMX_OUT_DVR; //表示數據輸出到DVR設備 
	pesfilter.pes_type = DMX_PES_AUDIO3;  //表示這一路pid數據是音頻數據
	pesfilter.flags = DMX_IMMEDIATE_START; //常規設置,不需要改變
	ioctl(dmxfd, DMX_SET_PES_FILTER, &pesfilter)

  
  
  經過前面幾步,初始化設置就完成了,數據就會上來,這裏就可以進行循環讀寫了
這裏一共要讀兩種數據,
1,TS流數據,這個就是音視頻文件的數據,直接存入文件系統即可。
2,index數據,此數據主要描述音視頻的幀類型和時間碼 ,經過二次封裝,形成GOP信息,存入文件系統即可。


While()
{
Read index data from index_dvr
Analysis index data, produce GOP information
Save the GOP information to GOP file
Read TS data from dvr
Write TS data to DVR
}


PVR播放典型例程



1,打開dvr設備
dev/dvb/adapter0/dvr0
dvb_dvr_open

2,demux設置:
dev/dvb/adapter0/demux0
設置PES過濾所需PID,以及TS流輸出方向


3,設置trustzone解密 


4,進入循環,從文件讀,向DVR寫
While()
{
Read file
Write DVR
}


經過前面幾步,初始化設置就完成了,這裏就可以進行循環讀寫,將數據送入DVR
這裏一共要處理兩種數據,
1,讀GOP信息,從中得到音視頻幀的位置,時間碼等信息。
2,根據得到的音視頻幀位置,從TS文件中讀出音視頻幀,送入DVR。


While()
{
Read GOP file
Analysis GOP data,
Get the correct location of TS file
Read TS data from the correct location of TS file
Write TS data to DVR
}





PVR幾個特殊場景

1,節目具有多路音頻的場景


例如有兩路音頻,audio_1 ,audio_2
設置demux  pid filter的時候,要如下設置:
設置audio_1  的 pesfilter.pes_type = DMX_PES_AUDIO3;  
設置audio_2  的 pesfilter.pes_type = DMX_PES_OTHER;

如果還有更多路音頻,例如三路以上,只需要把多出來的音頻均設置成 DMX_PES_OTHER即可。


2,Audio_only場景
audio——only是隻錄製音頻,不錄製視頻的場景,
此時設置index設備結點屬性的時候,視頻格式做如下設置:
index_config.video_format = DVR_VIDEO_FAMAT_NONE;




DVB 核心代碼



Kernal/drivers/media/dvb-core
Linux自身定義的DVB框架性代碼。
主要實現設備註冊,設備讀寫,回調函數,內存管理等框架性代碼
從linux框架的角度規定了對設備文件讀寫控制,不關心上層的具體業務,只處理設備文件相關操作。


Kernal/drivers/media/platform/sdp/sdp_demux
Dvb功能實現的具體代碼。與設備硬件強相關。
是dvb-core代碼中各種掛接函數的具體實現。
負責操作管理硬件。
響應dvb-core傳遞下來的各種上層業務,通過對硬件的操作實際完成各種具體操作。


Dvb-core 代碼介紹 


Dvbdev.c
定義了DVB各設備的註冊函數。
便於實際初始化函數調用。
Dmxdev.c
定義了dvb中demux、dvr、index等設備文件的操作函數框架。
例如open、ioctl、read等函數的大致形式就定義在此。其中含多函數都採用了函數指針的形式,指向具體的函數實現。
Dvb_demux.c
對demux設備文件使用到的主要結構體進行初始化。對demux的feed、filter等功能定義了框架代碼。
Dvb_ringbuffer.c
定義了DVB-core所使用的環形內存讀寫管理方式

Demux 驅動運行流程


demux的數據輸出爲PVR寄存器

PVR硬件模塊解封裝TS流,輸出ES流到解碼器MFC


同時demux模塊還能將TS流用DMA方式輸出到到內存dvb_bufferring, 便於TS流讀操作,錄製TS 流

PVR還能接收dvb_bufferring用DMA方式送過來的數據流,解封TS流,輸出ES流到解碼器


dvb_bufferring的驅動運轉是靠中斷驅動的。


如果此時是在寫PVR,

PVR 模塊中的ES流輸出給解碼器後,PVR的中斷會告知現在PVR中有空間可用,DMA開始寫入

DMA完畢的中斷髮生後,觸發回調函數,從dvb_bufferring 裏面更新一下環,

同時通知用戶態現在有空間可以寫入了,

DMA的目的地址是PVR模塊地址,DMA源地址是更新完畢的dvb_bufferring裏面的數據區



如果此時是在讀PVR,

demux模塊中的TS包數量達到閾值後,產生中斷,開始DMA到dvb_bufferring

DMA完畢後,DMA中斷產生,從dvb_bufferring 裏面更新一下環,

DMA的目的地址是dvb_bufferring裏面的數據區,DMA源地址是demux寄存器



ES流buffer只有一塊,PVR往裏面DMA。



MFC解碼足夠快,1毫秒
MFC解碼完後,有個PCR硬件模塊,接收TS流裏的時間同步信息。根據這個同步信息不斷對自己調整

MFC在DTV模式下,會告訴PCR模塊,這一幀的PTS和解碼完比的地址。

PCR對時間做一下調整,就會通知顯示模塊顯示


這裏MFC用了多buffer技術,防止屏幕撕裂
DTV解碼時,會告訴MFC現在是DTV模式,不是MM模式





  
  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章