注:此前寫了一些列的分析RTMPdump(libRTMP)源代碼的文章,在此列一個清單:
RTMPdump 源代碼分析 1: main()函數
RTMPDump(libRTMP)源代碼分析 2:解析RTMP位址——RTMP_ParseURL()
RTMPdump(libRTMP) 源代碼分析 3: AMF編碼
RTMPdump(libRTMP)源代碼分析 4: 連接配接第一步——握手(Hand Shake)
RTMPdump(libRTMP) 源代碼分析 5: 建立一個流媒體連接配接 (NetConnection部分)
RTMPdump(libRTMP) 源代碼分析 6: 建立一個流媒體連接配接 (NetStream部分 1)
RTMPdump(libRTMP) 源代碼分析 7: 建立一個流媒體連接配接 (NetStream部分 2)
RTMPdump(libRTMP) 源代碼分析 8: 發送消息(Message)
RTMPdump(libRTMP) 源代碼分析 9: 接收消息(Message)(接收視音頻資料)
RTMPdump(libRTMP) 源代碼分析 10: 處理各種消息(Message)
===============================
之前分析了一下RTMPDump的Main()函數,其中擷取RTMP流媒體資料很重要的前提是RTMP的URL的解析。如果沒有這一步,那程式在強大也是白搭。現在來解析一下這個函數吧:RTMP_ParseURL()。
下面首先回顧一下RTMP的URL的格式:
rtmp://localhost/vod/mp4:sample1_1500kbps.f4v
“://”之前的是使用的協定類型,可以是rtmp,rtmpt,rtmps等
之後是伺服器位址
再之後是端口号(可以沒有,預設1935)
在之後是application的名字,在這裡是“vod”
最後是流媒體檔案路徑。
關于URL就不多說了,可以檢視相關文檔,下面貼上注釋後的代碼(整個parseurl.c):
/*
* 本檔案主要包含了對輸入URL的解析
*/
#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "rtmp_sys.h"
#include "log.h"
/*解析URL,得到協定名稱(protocol),主機名稱(host),應用程式名稱(app)
*
*/
int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
AVal *playpath, AVal *app)
{
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;
/* 字元串解析 */
/* 查找“://” */
//函數原型:char *strstr(char *str1, char *str2);
//功能:找出str2字元串在str1字元串中第一次出現的位置(不包括str2的串結束符)。
//傳回值:傳回該位置的指針,如找不到,傳回空指針。
p = strstr((char *)url, "://");
if(!p) {
RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
return FALSE;
}
{
//指針相減,傳回“://”之前字元串長度len
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);
parsehost:
//擷取主機名稱
//跳過“://”
p+=3;
/* 檢查一下主機名 */
if(*p==0) {
RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
return FALSE;
}
//原型:char *strchr(const char *s,char c);
//功能:查找字元串s中首次出現字元c的位置
//說明:傳回首次出現c的位置的指針,如果s中不存在c則傳回NULL。
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;
}
/* 擷取端口号 */
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;
{
/* 擷取應用程式(application)
*
* rtmp://host[:port]/app[/appinstance][/...]
* application = app[/appinstance]
*/
char *slash2, *slash3 = NULL;//指向第二個斜杠,第三個斜杠的指針
int applen, appnamelen;
slash2 = strchr(p, '/');//指向第二個斜杠
if(slash2)
slash3 = strchr(slash2+1, '/');//指向第三個斜杠,注意slash2之是以+1是因為讓其後移一位
applen = end-p; /* ondemand, pass all parameters as app */
appnamelen = applen; /* ondemand length */
if(ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
appnamelen = ques-p;
}
else if(strncmp(p, "ondemand/", 9)==0) {
/* app = ondemand/foobar, only pass app=ondemand */
applen = 8;
appnamelen = 8;
}
else { /* app!=ondemand, so app is app[/appinstance] */
if(slash3)
appnamelen = slash3-p;
else if(slash2)
appnamelen = slash2-p;
applen = appnamelen;
}
app->av_val = p;
app->av_len = applen;
RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
p += appnamelen;
}
if (*p == '/')
p++;
if (end-p) {
AVal av = {p, end-p};
RTMP_ParsePlaypath(&av, playpath);
}
return TRUE;
}
/*
* 從URL中擷取播放路徑(playpath)。播放路徑是URL中“rtmp://host:port/app/”後面的部分
*
* 擷取FMS能夠識别的播放路徑
* mp4 流: 前面添加 "mp4:", 删除擴充名
* mp3 流: 前面添加 "mp3:", 删除擴充名
* flv 流: 删除擴充名
*/
void RTMP_ParsePlaypath(AVal *in, AVal *out) {
int addMP4 = 0;
int addMP3 = 0;
int subExt = 0;
const char *playpath = in->av_val;
const char *temp, *q, *ext = NULL;
const char *ppstart = playpath;
char *streamname, *destptr, *p;
int pplen = in->av_len;
out->av_val = NULL;
out->av_len = 0;
if ((*ppstart == '?') &&
(temp=strstr(ppstart, "slist=")) != 0) {
ppstart = temp+6;
pplen = strlen(ppstart);
temp = strchr(ppstart, '&');
if (temp) {
pplen = temp-ppstart;
}
}
q = strchr(ppstart, '?');
if (pplen >= 4) {
if (q)
ext = q-4;
else
ext = &ppstart[pplen-4];
if ((strncmp(ext, ".f4v", 4) == 0) ||
(strncmp(ext, ".mp4", 4) == 0)) {
addMP4 = 1;
subExt = 1;
/* Only remove .flv from rtmp URL, not slist params */
} else if ((ppstart == playpath) &&
(strncmp(ext, ".flv", 4) == 0)) {
subExt = 1;
} else if (strncmp(ext, ".mp3", 4) == 0) {
addMP3 = 1;
subExt = 1;
}
}
streamname = (char *)malloc((pplen+4+1)*sizeof(char));
if (!streamname)
return;
destptr = streamname;
if (addMP4) {
if (strncmp(ppstart, "mp4:", 4)) {
strcpy(destptr, "mp4:");
destptr += 4;
} else {
subExt = 0;
}
} else if (addMP3) {
if (strncmp(ppstart, "mp3:", 4)) {
strcpy(destptr, "mp3:");
destptr += 4;
} else {
subExt = 0;
}
}
for (p=(char *)ppstart; pplen >0;) {
/* skip extension */
if (subExt && p == ext) {
p += 4;
pplen -= 4;
continue;
}
if (*p == '%') {
unsigned int c;
sscanf(p+1, "%02x", &c);
*destptr++ = c;
pplen -= 3;
p += 3;
} else {
*destptr++ = *p++;
pplen--;
}
}
*destptr = '\0';
out->av_val = streamname;
out->av_len = destptr - streamname;
}
rtmpdump源代碼(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561
rtmpdump源代碼(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163