新聞中心
隨著網(wǎng)絡(luò)技術(shù)的不斷進(jìn)步,視頻傳輸已經(jīng)成為人們生活中不可或缺的一部分。RTSP(Real Time Streaming Protocol)作為一種用于顯示多媒體數(shù)據(jù)的應(yīng)用層協(xié)議,廣泛應(yīng)用于視頻傳輸領(lǐng)域。而在視頻傳輸?shù)倪^程中,如果需要同時向多個用戶傳輸展示同一視頻信息,使用組播(Multicast)技術(shù)將是比較明智的選擇。本篇文章將介紹如何使用。

在網(wǎng)站制作、成都網(wǎng)站設(shè)計過程中,需要針對客戶的行業(yè)特點、產(chǎn)品特性、目標(biāo)受眾和市場情況進(jìn)行定位分析,以確定網(wǎng)站的風(fēng)格、色彩、版式、交互等方面的設(shè)計方向。創(chuàng)新互聯(lián)還需要根據(jù)客戶的需求進(jìn)行功能模塊的開發(fā)和設(shè)計,包括內(nèi)容管理、前臺展示、用戶權(quán)限管理、數(shù)據(jù)統(tǒng)計和安全保護(hù)等功能。
一、組播簡介
組播是一種數(shù)據(jù)傳輸方式,其將同一組播組內(nèi)的數(shù)據(jù)只發(fā)送一份,讓組播組內(nèi)的所有接收端共享數(shù)據(jù)。與廣播(Broadcast)不同,組播只向那些負(fù)責(zé)請求接收數(shù)據(jù)的多播組成員發(fā)送數(shù)據(jù)。組播在傳輸大規(guī)模多媒體數(shù)據(jù)及多點數(shù)據(jù)傳輸時有很好的效果。在IP協(xié)議中使用IGMP(Internet Group Management Protocol)協(xié)議作為組播協(xié)議。
二、RTSP協(xié)議
RTSP協(xié)議是用于在IP網(wǎng)絡(luò)中控制多媒體數(shù)據(jù)的流協(xié)議。通過RTSP協(xié)議控制的流可以由Real Player、Media Player、QuickTime Player等多媒體播放器來播放。RTSP協(xié)議使用TCP傳輸控制命令,并使用UDP或TCP傳輸多媒體數(shù)據(jù)。其基本控制命令包括:DESCRIBE、SETUP、PLAY、PAUSE、TEARDOWN、GET_PARAMETER、SET_PARAMETER。
三、RTSP組播技術(shù)實現(xiàn)
1. 創(chuàng)建RTSP TCP監(jiān)聽接口
使用Linux C語言的socket庫可以創(chuàng)建TCP監(jiān)聽,并綁定一個端口等待客戶端的請求連接。
“`
/* 創(chuàng)建RTSP TCP監(jiān)聽接口 */
#include
#include
#include
#define RTSP_PORT 554
int mn(int argc, char *argv[]) {
int rtsp_sockfd;
struct sockaddr_in rtsp_addr;
/* 創(chuàng)建TCP監(jiān)聽socket */
rtsp_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (rtsp_sockfd == -1) {
perror(“Fled to create RTSP listen socket”);
return -1;
}
/* 創(chuàng)建監(jiān)聽地址 */
rtsp_addr.sin_family = AF_INET;
rtsp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
rtsp_addr.sin_port = htons(RTSP_PORT);
/* 綁定socket到監(jiān)聽地址 */
if (bind(rtsp_sockfd, (struct sockaddr*)(&rtsp_addr), sizeof(struct sockaddr)) == -1) {
perror(“Fled to bind RTSP listen socket”);
return -1;
}
/* 開始監(jiān)聽 */
if (listen(rtsp_sockfd, 5) == -1) {
perror(“Fled to listen on RTSP listen socket”);
return -1;
}
return 0;
}
“`
2. 支持DESCRIBE命令
RTSP的DESCRIBE命令返回一個SDP(Session Description Protocol)描述,它包含有關(guān)媒體流的信息,例如媒體的類型、格式、連接信息等。在組播場景下,需要將SDP中的unicast地址替換為組播地址。
“`
/* 支持DESCRIBE命令 */
#include
#include
#include
#include
#define DESC_FILE “video.sdp”
void handle_describe(int sockfd, struct sockaddr_in *client_addr) {
char sdp_buf[2023], send_buf[8192];
int sdp_fd, rlen;
/* 打開sdp文件 */
sdp_fd = open(DESC_FILE, O_RDON);
if (sdp_fd == -1) {
perror(“Fled to open SDP file”);
return;
}
/* 讀取sdp文件內(nèi)容 */
rlen = read(sdp_fd, sdp_buf, sizeof(sdp_buf));
if (rlen == -1) {
perror(“Fled to read SDP file”);
close(sdp_fd);
return;
}
/* 關(guān)閉sdp文件 */
close(sdp_fd);
/* 組裝響應(yīng)內(nèi)容 */
sprintf(send_buf, “RTSP/1.0 200 OK\r\n”
“Content-Type: application/sdp\r\n”
“Content-Base: rtsp://%s:%d/\r\n”
“Server: My RTSP Server/1.0\r\n”
“%s”,
inet_ntoa(client_addr->sin_addr),
ntohs(client_addr->sin_port),
sdp_buf);
/* 發(fā)送響應(yīng)內(nèi)容 */
send(sockfd, send_buf, strlen(send_buf), 0);
}
“`
需要注意的是,SDP中的unicast地址需要替換為組播地址。在SDP中有如下格式的信息:
“`
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0IAH5WoFAFuQA==,aM4wpIA=
c=IN IP4 192.168.1.100 // 該字段為unicast地址
“`
我們需要將其修改為:
“`
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0IAH5WoFAFuQA==,aM4wpIA=
c=IN IP4 224.0.1.100 // 該字段修改為組播地址
“`
3. 獲取組播地址
組播地址的選取需滿足以下規(guī)定:
– 組播地址為D類IP地址,其地址范圍為224.0.0.0-239.255.255.255
– 組播地址的之一字節(jié)必須為224-239,即之一字節(jié)范圍為0xE0-0xEF
– 組播地址的第二字節(jié)為用戶自選值,一般選用0~255中未重復(fù)的數(shù)字
– 組播地址的第三、四字節(jié)分別表示組播組的ID,可以使用隨機(jī)數(shù)或用戶手動指定
在C程序中,可以使用rand()函數(shù)生成一個隨機(jī)值。
“`
/* 獲取組播地址 */
#include
char *get_multi_addr() {
char *addr;
int b1, b2, b3;
/* 隨機(jī)生成組播地址 */
b1 = 224 + rand() % 16;
b2 = rand() % 256;
b3 = rand() % 256;
/* 組裝組播地址 */
addr = malloc(16);
sprintf(addr, “%d.%d.%d.100”, b1, b2, b3);
return addr;
}
“`
4. 支持SETUP命令
RTSP的SETUP命令用于請求服務(wù)器初始化一個數(shù)據(jù)流。在組播場景下需要根據(jù)客戶端的交互來確定需要向哪個組播地址發(fā)送數(shù)據(jù)。當(dāng)收到SETUP命令后,服務(wù)器將分配一個新的組播地址,并發(fā)送響應(yīng)內(nèi)容包含組播地址以及RTP傳輸端口等。此時,在RTP傳輸端口上,服務(wù)器開始不斷向組播地址的RTP端口發(fā)送數(shù)據(jù)包。
我們需要在關(guān)鍵的設(shè)置中獲取到組播地址和對應(yīng)的端口號,并使用UDP傳輸媒體數(shù)據(jù)。
“`
/* 支持SETUP命令 */
#include
#include
#include
#define RTP_PORT 47000
void handle_setup(int sockfd, struct sockaddr_in *client_addr) {
char *multi_addr, send_buf[512];
struct timeval tv;
int rtp_fd, ctrl_fd, send_len, addr_len;
struct sockaddr_in rtp_addr, ctrl_addr;
/* 創(chuàng)建rtp組播socket */
rtp_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (rtp_fd == -1) {
perror(“Fled to create RTP listen socket”);
return;
}
/* 創(chuàng)建rtp地址 */
rtp_addr.sin_family = AF_INET;
rtp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
rtp_addr.sin_port = htons(RTP_PORT);
/* 綁定rtp socket到rtp地址 */
if (bind(rtp_fd, (struct sockaddr*)(&rtp_addr), sizeof(struct sockaddr)) == -1) {
perror(“Fled to bind RTP listen socket”);
return;
}
/* 獲取組播地址 */
srand(time(NULL));
multi_addr = get_multi_addr();
/* 創(chuàng)建ctrl組播socket */
ctrl_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (ctrl_fd == -1) {
perror(“Fled to create CTRL socket”);
return;
}
/* 創(chuàng)建ctrl地址 */
ctrl_addr.sin_family = AF_INET;
ctrl_addr.sin_addr.s_addr = inet_addr(multi_addr);
ctrl_addr.sin_port = htons(RTP_PORT + 1);
/* 設(shè)置組播選項 */
unsigned char ttl = 32;
setsockopt(ctrl_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
unsigned char loop_on = 1;
setsockopt(ctrl_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop_on, sizeof(loop_on));
/* 發(fā)送控制內(nèi)容 */
sprintf(send_buf, “RTSP/1.0 200 OK\r\n”
“Session: 12345678\r\n”
“Transport: RTP/AVP;unicast;client_port=50000-50001;mode=play\r\n”
“Server: My RTSP Server/1.0\r\n”
“Content-Length: 0\r\n\r\n”);
send_len = strlen(send_buf);
addr_len = sizeof(struct sockaddr_in);
sendto(sockfd, send_buf, send_len, 0, (struct sockaddr*)&(*client_addr), addr_len);
/* 發(fā)送控制包(組播) */
gettimeofday(&tv, NULL);
int now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
int last_frame = now;
int frame_rate = 25;
while (1) {
send_len = rtp_send_frame(multi_addr, RTP_PORT, now, last_frame, frame_rate);
last_frame = now;
send_len = sendto(rtp_fd, rtp_buf, send_len, 0, (struct sockaddr*)&ctrl_addr, addr_len);
usleep(40000);
}
}
“`
5. RTP數(shù)據(jù)幀發(fā)送
在處理SETUP命令時,我們開始在指定的組播地址和端口號上不斷發(fā)送數(shù)據(jù)包。那么在每個數(shù)據(jù)包中包含了什么樣的信息呢?我們在創(chuàng)建數(shù)據(jù)包時需要注意以下幾點:
– 前12字節(jié)為RTP頭部,其中標(biāo)識傳輸類型、傳輸時間等關(guān)鍵信息
– 前12字節(jié)之后的數(shù)據(jù)包為媒體數(shù)據(jù)
我們使用最簡單的方式生成一個視頻數(shù)據(jù)包,其中數(shù)據(jù)的pattern為所有像素為0x80的視頻幀。
“`
/* RTP數(shù)據(jù)包發(fā)送 */
#define MAX_RTP_PKT_LENGTH 1400
#define H264_PAYLOAD_TYPE 96 /* 參考RFC3984 */
char rtp_buf[MAX_RTP_PKT_LENGTH];
int rtp_send_frame(const char *p_multi_ip, unsigned short port, int current_time, int last_time, int frame_rate) {
unsigned int len = 150000; // 150KB 視頻數(shù)據(jù)大小(假設(shè))
int nalu_payload_size = len – 4;
int nal_payload_position = 4;
/* 生成RTP頭部 */
memcpy(rtp_buf, “$$__header__$$”, 12);
/* 填充NALU信息 */
rtp_buf[0] = rtp_buf[0] | 0x80; // 填充RTP版本位和標(biāo)志位
rtp_buf[1] = rtp_buf[1] & 0x7F; // 填充長度位(更高位為1則表示后面有padding)
rtp_buf[1] = rtp_buf[1] | ((nalu_payload_size & 0x1FE00) >> 13);
rtp_buf[2] = (nalu_payload_size & 0x1FC0) >> 6;
rtp_buf[3] = (nalu_payload_size & 0x3F)
/* 填充NALU內(nèi)容 */
for(int s = nal_payload_position; s
rtp_buf[s] = 0x80; //0x80 是數(shù)據(jù)位
}
return nal_payload_position + nalu_payload_size;
}
“`
四、
相關(guān)問題拓展閱讀:
- 我想在linux下寫一個c程序調(diào)用linux的可執(zhí)行文件或者程序,怎么做
我想在linux下寫一個c程序調(diào)用linux的可執(zhí)行文件或者程序,怎么做
Linux C編程中,調(diào)用另一個
可執(zhí)行文件
或調(diào)用命令用system函判如行數(shù)最簡單了,這個函數(shù)原理是在你編寫的那個程橡敏序的內(nèi)部啟動另一個程序或命令,從而創(chuàng)建一個新進(jìn)程,并等待這個進(jìn)程執(zhí)行完畢退出。如果正常執(zhí)行,system函數(shù)掘嘩將返回被執(zhí)行程序或命令的退出碼;如果無法運(yùn)行這個程序或命令,將返回錯誤代碼127;如果是其他錯誤,返回-1。這個函數(shù)的原型是:
#include
int system(const char *string);
參數(shù)string是將要執(zhí)行的
程序文件
名或路徑,如果是啟動一個命令就是一個命令
字符串
。
還有一種執(zhí)行外部程序的方法是exec系列函數(shù),一般是在fork的子進(jìn)程里面調(diào)用exec系列函數(shù),那主進(jìn)程里直接調(diào)用exec系列不行嗎,為什么要fork再在子進(jìn)程里調(diào)用呢?因為exec系列的函數(shù)(包括execl函數(shù))是將當(dāng)前進(jìn)程替換成新進(jìn)程,這里的當(dāng)前進(jìn)程就是你編寫的程序,也就是說新進(jìn)程啟動后調(diào)用exec函數(shù)的進(jìn)程就不存在了,所以exec系列函數(shù)調(diào)用之后的代碼就不會再執(zhí)行了。如果你不放在fork子進(jìn)程里面,那你編寫的程序的主進(jìn)程在執(zhí)行execl函數(shù)后就完全不存在了,所以exec系列函數(shù)的使用都是先fork然后在子進(jìn)程里面調(diào)用。因為exec系列函數(shù)都要使用fork調(diào)用,所以我一般是用system函數(shù)。
linux c使用rtsp組播的介紹就聊到這里吧,感謝你花時間閱讀本站內(nèi)容,更多關(guān)于linux c使用rtsp組播,Linux C編程實現(xiàn)RTSP組播技術(shù),我想在linux下寫一個c程序調(diào)用linux的可執(zhí)行文件或者程序,怎么做的信息別忘了在本站進(jìn)行查找喔。
香港服務(wù)器選創(chuàng)新互聯(lián),2H2G首月10元開通。
創(chuàng)新互聯(lián)(www.cdcxhl.com)互聯(lián)網(wǎng)服務(wù)提供商,擁有超過10年的服務(wù)器租用、服務(wù)器托管、云服務(wù)器、虛擬主機(jī)、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗。專業(yè)提供云主機(jī)、虛擬主機(jī)、域名注冊、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。
新聞標(biāo)題:LinuxC編程實現(xiàn)RTSP組播技術(shù)(linuxc使用rtsp組播)
分享鏈接:http://m.fisionsoft.com.cn/article/cdpiido.html


咨詢
建站咨詢
