天天看點

海思項目學習記錄 -3、ORTP庫傳輸

1、ORTP庫安裝編譯

注意;要增加H.264的payload支援。

在src/avprofile.c中357行添加:

rtp_profile_set_payload(profile,96,&payload_type_h264);//這個96是h.264幀頭的一個資訊

配置和編譯、安裝

(1)進入ortp目錄執行./autogen.sh

(2)錯誤1:./autogen.sh: line 44: libtoolize: command not found

解決:sudo apt-get install libtool* 注意安裝的時候要保證虛拟機聯網

(2)錯誤2:libtoolize: error: Please install GNU M4, or ‘export M4=/path/to/gnu/m4’.

解決:sudo apt-get install m4

(3)錯誤3:Automake - aclocal: command not found

解決:sudo apt-get install automake

(4)繼續執行./configure --prefix=/tmp/ortp --host=arm-hisiv300-linux

prefix 安裝目錄,,host 交叉編譯工具鍊的選擇

(5)make && make install

最後;到/tmp/ortp目錄下檢視移植好的庫和頭檔案,之後在使用的時候要包含ku檔案和頭檔案才能正确使用。

注意安裝的時候要保證虛拟機聯網

/etc/network/interface檔案

ifdown ens33

ifup ens33

2、合并到mmp的sample 編譯運作

修改代碼後

重新編譯sample

(1)複制ortp頭檔案 cp /tmp/ortp/include/ortp/ ./ -rf

(2)修改venc中Makefile,添加libortp的連結支援 -lortp -L路徑

$(TARGET):%:%.o $(COMM_OBJ)

$(CC) $(CFLAGS) -lpthread -lm -lortp -o [email protected] $^ $(MPI_LIBS) $(AUDIO_LIBA) $(SENSOR_LIBS) -L/tmp/ortp/lib

(3)venc下執行make

即可生成可執行程式,注意要把ortp庫的.h檔案和lib庫部署到開發闆,否則運作的時候會提示缺少庫的。

3、ORTP庫源碼學習

ORTP庫概覽

(1)庫本身沒有main,提供一堆功能函數,都在src目錄下

(2)庫的使用給了案例,有main,在src/tests目錄下,每個.c檔案就是一個案例

(3)相關資料結構和頭檔案在include/ortp目錄下

(4)ortp實作了rtp和rtcp協定,前者負責傳輸,後者rtcp是rtp庫的補充負責控制和同步協調。

3.1、以rtp_send.c為案例進行了解ORTP庫

第一步;主要完成對payload的注冊
	av_profile_init(&av_profile)
		rtp_profile_set_payload(profile,96,&payload	type_h264);
第二步;初始化排程器
	ortp_scheduler_init();
		rtp_scheduler_new
			ortp_malloc
			rtp_scheduler_init 初始化互斥鎖、條件鎖,以及會話的初始化
		rtp_scheduler_start
			ortp_thread_create 互斥鎖保護,條件鎖等待建立線程
				rtp_scheduler_schedule 線程函數、
第三步;建立session和設定會話屬性
	rtp_session_new
		ortp_malloc
		rtp_session_init
			rtp_session_set_profile 最終還是要設定會話的profile
			rtp_session_enable_jitter_buffer 配置jitter防抖
			rtp_session_set_ssrc 判斷設定ssrc同步源标志
第四步;讀取發送
	rtp_session_send_with_ts
		rtp_session_create_packet 給資料添加幀頭
		rtp_session_sendm_with_ts 加了m表示完成格式;幀頭+内容
			rtp_session_rtp_send
				rtp_sendmsg or sendto
			rtp_session_rtcp_process_send
				rtp_sendmsg or sendto
           

細節部分;

ORTP中的排程器

排程器就是一個仲裁機構,也就是一段代碼,每隔一段時間執行一次決定排程誰,一般是線程或程序

ortp庫裡面的scheduler就是一個定時器,每隔多少時間執行一次、這裡面的排程實在幹嘛的?、ortp庫可以同時維持多路會話也就是多路通信,那麼就要需要排程運作保持分時複用

會話session ORTP庫的通信都是基于會話的,但是同時可以支援多個session同時進行,在代碼邏輯上

一個session最終都會展現到一段代碼一個資料結構就是結構體。

SSRC 同步源标志 這個可以用于會話通信的标志,當多路會話時用于可以根據資訊識别是那個會話

就是每個會話通信傳輸資訊的唯一識别碼、作用就是在多路會話的時候直接截取資訊可以根據ssrc知道是哪路會話的否則不知道。

jitter buffer防抖

jitter buffer技術是ip 音視訊通信裡相對比較進階的主題, jitter buffer子產品好壞通常是衡量一個voip用戶端/伺服器好壞的技術點之一,尤其是在網絡抖動比較嚴重,如3g,wifi環境,資料包的rtt值不均衡往往會導緻語音卡頓,丢字等現象,jitter buffer 子產品通過緩存一段資料包,把資料包重排,并均勻的送給播放端,一個好的jitter buffer實作通長是動态調整緩存大小的,在網絡延遲大,抖動嚴重時會動态增加緩存大小,在網絡恢複時動态減小緩存大小以減少端到端的播放延遲。

3.2、ORTP庫中create_and_bind函數

static ortp_socket_t create_and_bind

涉及到

struct addrinfo解釋

https://www.cnblogs.com/likui360/p/6128938.html

AF_XX 與PF_XX 位址族和協定族

setsockopt設定SO_REUSEADDR

https://blog.csdn.net/u010144805/article/details/78579528

3.3、RTP發送實驗源碼分析

首先要在common/sample_common_venc.c中添加ortp庫的初始化函數和發送函數;

然後再就是修改venc/sample_venc.c;mmp初始化編碼的步驟都不需要變,隻需要修改之前存放到檔案裡面去,現在是使用ortp包發送。

venc/sample_venc.c

//venc/sample_venc.c
HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    HI_S32 i;

    
    for (i = 0; i < pstStream->u32PackCount; i++)
    {
    //隻需要修改這裡
    	#if ORTP_ENABLE
		rtpSend(pRtpSession,pstStream->pstPack[i].pu8Addr, pstStream->pstPack[i].u32Len);
       #else
	   //檔案則需要考慮偏移量,而ortp庫則是網絡流每次放到原地即可
        	fwrite(pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,
               pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset, 1, fpH264File);

        	fflush(fpH264File);
        #endif
    }
           

sample_common_venc.c中

sample_common_venc.c中
//定義一個宏來管理。
#if ORTP_ENABLE
#include <ortp/ortp.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#define Y_PLOAD_TYPE 96 //H.264
#define MAX_RTP_PKT_LENGTH 1400
#define DefaultTimestampIncrement 3600 //(90000/25)
uint32_t g_userts=0;
RtpSession *pRtpSession = NULL;

#define LOCAL_HOST_IP  "192.168.1.20"

/**  初始化   
 *     
 *   主要用于對ortp以及其它參數進行初始化   
 *   @param:  char * ipStr 目的端IP位址描述串   
 *   @param:  int port 目的端RTP監聽端口   
 *   @return:  RtpSession * 傳回指向RtpSession對象的指針,如果為NULL,則初始化失敗   
 *   @note:      這個注意是從ortp-master\src\tests\rtpsend.c抄過來的
 */   
RtpSession * rtpInit( char  * ipStr, int  port)
{
    RtpSession *session; 
    char  *ssrc;
    printf("********oRTP for H.264 Init********\n");

    ortp_init();
    ortp_scheduler_init();
    ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);
    session=rtp_session_new(RTP_SESSION_SENDONLY);	

    rtp_session_set_scheduling_mode(session,1);
    rtp_session_set_blocking_mode(session,0);
    //rtp_session_set_connected_mode(session,TRUE);
    rtp_session_set_remote_addr(session,ipStr,port);//設定遠端IP和端口号 端口号就要兩邊一樣可以通信就行了
    rtp_session_set_payload_type(session,Y_PLOAD_TYPE);

    ssrc=getenv("SSRC");
    if (ssrc!=NULL) {
        printf("using SSRC=%i.\n",atoi(ssrc));
        // 設定輸出流的SSRC。不做此步的話将會給個随機值 
        rtp_session_set_ssrc(session,atoi(ssrc));
    }
    return  session;
}
/**  結束ortp的發送,釋放資源   
 *     
 *   @param:  RtpSession *session RTP會話對象的指針   
 *   @return:  0表示成功   
 *   @note:       
 */    
int  rtpExit(RtpSession *session)   
{ 
    printf("********oRTP for H.264 Exit********\n");  
    g_userts = 0;   
       
    rtp_session_destroy(session);   
    ortp_exit();   
    ortp_global_stats_display();   
  
     return  0;   
}   
/**  發送rtp資料包   
 *     
 *   主要用于發送rtp資料包   
 *   @param:  RtpSession *session RTP會話對象的指針   
 *   @param:  const char *buffer 要發送的資料的緩沖區位址   
  *   @param: int len 要發送的資料長度   
 *   @return:  int 實際發送的資料包數目   
 *   @note:     如果要發送的資料包長度大于BYTES_PER_COUNT,本函數内部會進行分包處理   
 */   
int  rtpSend(RtpSession *session, char  *buffer,  int  len)
{  
    int  sendBytes = 0; 
    int status;       
    uint32_t valid_len=len-4;
    unsigned char NALU=buffer[4];
     //分包發送,并且還涉及到一點H.264幀頭的資訊
    //如果資料小于MAX_RTP_PKT_LENGTH位元組,直接發送:單一NAL單元模式
    if(valid_len <= MAX_RTP_PKT_LENGTH)
    {
        sendBytes = rtp_session_send_with_ts(session,
                                             &buffer[4],  //注意從第四個位元組開始 的H.264格式問題
                                             valid_len,
                                             g_userts);
    }
    else if (valid_len > MAX_RTP_PKT_LENGTH)
    {
        //切分為很多個包發送,每個包前要對頭進行處理,如第一個包
        valid_len -= 1;
        int k=0,l=0;
        k=valid_len/MAX_RTP_PKT_LENGTH;
        l=valid_len%MAX_RTP_PKT_LENGTH;
        int t=0;
        int pos=5;
        if(l!=0)
        {
            k=k+1;
        }
        while(t<k)//||(t==k&&l>0))
        {
            if(t<(k-1))//(t<k&&l!=0)||(t<(k-1))&&(l==0))//(0==t)||(t<k&&0!=l))
            {
                buffer[pos-2]=(NALU & 0x60)|28;//把包的幀類型設定為RTP類型
                buffer[pos-1]=(NALU & 0x1f);
                if(0==t)
                {
                    buffer[pos-1]|=0x80;
                }
                sendBytes = rtp_session_send_with_ts(session,
                                                     &buffer[pos-2],
                                                     MAX_RTP_PKT_LENGTH+2,
                                                     g_userts);
                t++;
                pos+=MAX_RTP_PKT_LENGTH;
            }
            else //if((k==t&&l>0)||((t==k-1)&&l==0))
            {
                int iSendLen;
                if(l>0)
                {
                    iSendLen=valid_len-t*MAX_RTP_PKT_LENGTH;
                }
                else
                    iSendLen=MAX_RTP_PKT_LENGTH;
                buffer[pos-2]=(NALU & 0x60)|28;
                buffer[pos-1]=(NALU & 0x1f);
                buffer[pos-1]|=0x40;//重要等級提1
                sendBytes = rtp_session_send_with_ts(session,
                                                     &buffer[pos-2],
                                                     iSendLen+2,
                                                     g_userts);
                t++;
            }
        }
    }

    g_userts += DefaultTimestampIncrement;//timestamp increase
    return  len;
}
#endif
           

4、VLC播放器的配置檔案sdp

配置檔案sdp;是一種格式,到時候VCL來以什麼格式來解析。

m=video 8080 RTP/AVP 96 m表示内容 8080端口 RTP傳輸協定 96傳輸内容的協定

a=rtpmap:96 H264

a=framerate:25 幀率

c=IN IP4 192.168.1.20 IPV4的格式化,和本地的IP位址

繼續閱讀