LibRTMP原始碼分析 1:解析URL
本系列文件分析LibRTMP原始碼,所用原始碼位於GitHubLibrtmp for Android & iOS,應該不是最新的LibRTMP原始碼,對於學習是夠用的。
小廣告:歡迎加入iOS Android 直播、全景播放技術討論群:459486016
檔案:parseurl.c
函式呼叫關係:
RTMP_ParseURL
---- RTMP_ParsePlaypath
1、分析RTMP_ParseURL
函式定義:
int RTMP_ParseURL(const char *url,
int *protocol,
AVal *host,
unsigned int *port,
AVal *playpath,
AVal *app)
URL的組成格式為:[協議]://[主機名]:[埠]/檔案路徑?[鍵]:[值]
RTMP URL的格式為:
rtmp://host[:port]/app[/appinstance][/...]
application = app[/appinstance]
1.1、解析協議
1.1.1、原始碼
char *p, *end, *col, *ques, *slash;
RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
*protocol = RTMP_PROTOCOL_RTMP;
*port = 0;
playpath->av_len = 0;
playpath->av_val = NULL;
app->av_len = 0;
app->av_val = NULL;
/* Old School Parsing */
/* look for usual :// pattern */
p = strstr(url, "://");
if (!p) {
RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
return FALSE;
}
{
int len = (int) (p - url);
if (len == 4 && strncasecmp(url, "rtmp", 4) == 0)
*protocol = RTMP_PROTOCOL_RTMP;
else if (len == 5 && strncasecmp(url, "rtmpt", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPT;
else if (len == 5 && strncasecmp(url, "rtmps", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPS;
else if (len == 5 && strncasecmp(url, "rtmpe", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPE;
else if (len == 5 && strncasecmp(url, "rtmfp", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMFP;
else if (len == 6 && strncasecmp(url, "rtmpte", 6) == 0)
*protocol = RTMP_PROTOCOL_RTMPTE;
else if (len == 6 && strncasecmp(url, "rtmpts", 6) == 0)
*protocol = RTMP_PROTOCOL_RTMPTS;
else {
RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
goto parsehost;
}
}
RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
1.1.2、流程分析
- 找出
://
所在位置。找不到則報錯:RTMP URL: No :// in url!。 - 若找到,則p為
:
在URL字串中的位置。 -
int len = (int) (p - url)
得到://
前面字串的長度,即協議的長度。 - 判斷協議。使用
strncasecmp
以忽略字串大小寫方式進行比較。 - 對於以
://
開頭的URL,預設為RTMP協議。
1.1.3、驗證
抽取如下程式碼並執行。
#define RTMP_FEATURE_HTTP 0x01
#define RTMP_FEATURE_ENC 0x02
#define RTMP_FEATURE_SSL 0x04
#define RTMP_FEATURE_MFP 0x08 /* not yet supported */
#define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
#define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */
#define RTMP_PROTOCOL_UNDEFINED -1
#define RTMP_PROTOCOL_RTMP 0
#define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
#define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
#define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
#define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC)
#define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL)
#define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP
char *p;
int ptl, *protocol = &ptl;
char *url = "rtmp://live.hkstv.hk.lxdns.com/live/hks";
p = strstr(url, "://");
if (!p) {
printf("RTMP URL: No :// in url!");
return FALSE;
}
{
int len = (int) (p - url);
if (len == 4 && strncasecmp(url, "rtmp", 4) == 0)
*protocol = RTMP_PROTOCOL_RTMP;
else if (len == 5 && strncasecmp(url, "rtmpt", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPT;
else if (len == 5 && strncasecmp(url, "rtmps", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPS;
else if (len == 5 && strncasecmp(url, "rtmpe", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMPE;
else if (len == 5 && strncasecmp(url, "rtmfp", 5) == 0)
*protocol = RTMP_PROTOCOL_RTMFP;
else if (len == 6 && strncasecmp(url, "rtmpte", 6) == 0)
*protocol = RTMP_PROTOCOL_RTMPTE;
else if (len == 6 && strncasecmp(url, "rtmpts", 6) == 0)
*protocol = RTMP_PROTOCOL_RTMPTS;
else {
printf("Unknown protocol!\n");
}
}
printf("Parsed protocol: %d", *protocol);
執行:
輸入:url | 輸出:protocol |
---|---|
"rtmptslive.hkstv.hk.lxdns.com/live/hks" | RTMP URL: No :// in url! |
"rtmp://live.hkstv.hk.lxdns.com/live/hks" | Parsed protocol: 0 |
"://live.hkstv.hk.lxdns.com/live/hks" | Unknown protocol! |
Parsed protocol: 0 | |
"rtmpts://live.hkstv.hk.lxdns.com/live/hks" | Parsed protocol: 5 |
協議解析結束,開始解析主機。
1.2、解析主機
1.2.1、原始碼
parsehost:
/* let's get the hostname */
p += 3;
/* check for sudden death */
if (*p == 0) {
RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
return FALSE;
}
end = p + strlen(p);
col = strchr(p, ':');
ques = strchr(p, '?');
slash = strchr(p, '/');
{
int hostlen;
if (slash)
hostlen = slash - p;
else
hostlen = end - p;
if (col && col - p < hostlen)
hostlen = col - p;
if (hostlen < 256) {
host->av_val = p;
host->av_len = hostlen;
RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
} else {
RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
}
p += hostlen;
}
1.2.2、流程分析
- 如果主機為空,則報錯No hostname in URL!。
-
end = p + strlen(p);
由p後移三個位置,跳過://
且位於主機字串起始點。由strlen(p)
計算出剩餘字串長度並加上起始位置,得到整個URL字串的終點位置。 - 跟緊主機字串結尾的
/
和:
字元不算在主機字串長度內。如果只有路徑,無埠,則主機字串的長度 = 路徑字串的起始位置 - 主機字串的起始位置。如果指定了埠,則主機字串的長度 = 路徑字串的起始位置 - 主機字串的起始位置。 - 由3得到的主機字串若超過255個字元,則警告Hostname exceeds 255 characters!。
1.2.3、驗證
抽取如下程式碼並執行。
/* let's get the hostname */
p += 3;
/* check for sudden death */
if (*p == 0) {
printf("No hostname in URL!\n");
return FALSE;
}
end = p + strlen(p);
printf("url length = %lu, p = %p, p length = %lu, end = %p\n", strlen(url), p, strlen(p), end);
col = strchr(p, ':');
ques = strchr(p, '?');
slash = strchr(p, '/');
printf("port = %s\nques = %s\nslash = %s\n", col, ques, slash);
{
int hostlen;
if (slash)
hostlen = slash - p;
else
hostlen = end - p;
if (col && col - p < hostlen)
hostlen = col - p;
if (hostlen < 256) {
printf("Parsed host : %.*s\n", hostlen, p);
} else {
printf("Hostname exceeds 255 characters!");
}
p += hostlen;
}
執行:
輸入:URL | 輸出:host |
---|---|
"rTmPe://live.hkstv.hk.lxdns.com/live/hks" | live.hkstv.hk.lxdns.com |
"rtmp://live.hkstv.hk.lxdns.com:2000/cardhouse/s3?e=1&starttime=000010" | live.hkstv.hk.lxdns.com |
其中,對於輸入URL"rtmp://live.hkstv.hk.lxdns.com:2000/cardhouse/s3?e=1&starttime=000010"
printf("url length = %lu, p = %p, p length = %lu, end = %p\n", strlen(url), p, strlen(p), end);
輸出url length = 68, p = 0x10d915e3a, p length = 61, end = 0x10d915e77
可驗證end為URL字串末尾。
port = :2000/cardhouse/s3?e=1&starttime=000010
ques = ?e=1&starttime=000010
slash = /cardhouse/s3?e=1&starttime=000010
即strchr
找出指定字元首次出現位置的指標。
主機解析結束,開始解析埠號、應用程式名或播放路徑。
1.3、解析埠號
1、原始碼
/* get the port number if available */
if (*p == ':') {
unsigned int p2;
p++;
p2 = atoi(p);
if (p2 > 65535) {
RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
} else {
*port = p2;
}
}
if (!slash) {
RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
return TRUE;
}
p = slash + 1;
2、分析
如果p指向:
字元,atoi
持續讀取字元,直到非數字字元為止,並將已讀取的數值當成十進位制處理,如
int i;
char *buffer = "31273a";
i = atoi (buffer);
printf ("The value entered is %d. Its double is %d.\n",i,i*2);
輸出:The value entered is 31273. Its double is 62546.
3、驗證
unsigned int prt, *port = &prt;
/* get the port number if available */
if (*p == ':') {
unsigned int p2;
p++;
p2 = atoi(p);
if (p2 > 65535) {
printf("Invalid port number!");
} else {
*port = p2;
printf("port = %u", *port);
}
} else {
printf("port does not exists.\n");
}
輸入:URL | 輸出:port |
---|---|
"rtmp://live.hkstv.hk.lxdns.com:2000/cardhouse/s3?e=1&starttime=000010" | 2000 |
"rtmp://live.hkstv.hk.lxdns.com/cardhouse/s3?e=1&starttime=000010" | 無 |
1.4、解析應用程式名或播放路徑
相關文章
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 1、ArrayList原始碼解析原始碼
- ThinkPHP6 原始碼閱讀(六):Url 解析PHP原始碼
- ReentrantLock解析及原始碼分析ReentrantLock原始碼
- AFNetworking原始碼解析系列(1)原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- 介面1原始碼分析原始碼
- Promise-Polyfill原始碼解析(1)Promise原始碼
- 原始碼分析axios(1)~原始碼分析、模擬axios的建立原始碼iOS
- 3.23 vchain原始碼分析1AI原始碼
- Netty Pipeline原始碼分析(1)Netty原始碼
- React Native 0.55.4 Android 原始碼分析(Java層原始碼解析)React NativeAndroid原始碼Java
- 原始碼分析系列1:HashMap原始碼分析(基於JDK1.8)原始碼HashMapJDK
- myBatis原始碼解析-日誌篇(1)MyBatis原始碼
- Android原始碼分析(LayoutInflater.from(this).inflate(resId,null);原始碼解析)Android原始碼Null
- Struts2 原始碼分析-----攔截器原始碼解析 --- ParametersInterceptor原始碼
- 友好 RxJava2.x 原始碼解析(三)zip 原始碼分析RxJava原始碼
- 3.21以太貓原始碼分析1原始碼
- rxjs 原始碼分析1-(fromEvent)JS原始碼
- newrelic python agent 原始碼分析-1Python原始碼
- EOS原始碼分析(1)安裝原始碼
- JDK 原始碼分析(1) Object類JDK原始碼Object
- SpringMVC原始碼分析1:SpringMVC概述SpringMVC原始碼
- 【JDK】JDK原始碼分析-AbstractQueuedSynchronizer(1)JDK原始碼
- AFL二三事 -- 原始碼分析 1原始碼
- k8s client-go原始碼分析 informer原始碼分析(1)-概要分析K8SclientGo原始碼ORM
- Spring原始碼解析02:Spring IOC容器之XmlBeanFactory啟動流程分析和原始碼解析Spring原始碼XMLBean
- ThinkPHP6 原始碼分析之解析 RequestPHP原始碼
- Django(49)drf解析模組原始碼分析Django原始碼
- RecyclerView 原始碼分析(一) —— 繪製流程解析View原始碼
- Spring Security原始碼分析六:Spring Social社交登入原始碼解析Spring原始碼
- 容器類原始碼解析系列(三)—— HashMap 原始碼分析(最新版)原始碼HashMap
- django-rest-framework原始碼分析2—認證(Authentication)原始碼解析DjangoRESTFramework原始碼
- React原始碼解析(1):jsx語法是如何解析React原始碼JS
- FutureTask原始碼解析(1)——預備知識原始碼
- SpringMVC原始碼解析(1)-啟動過程SpringMVC原始碼
- tomcat原始碼分析(第三篇 tomcat請求原理解析--Connector原始碼分析)Tomcat原始碼
- Java容器類框架分析(1)ArrayList原始碼分析Java框架原始碼
- 重拾RunLoop之原始碼分析1OOP原始碼