librtmp源码分析---url解析



#include <stdlib.h>
#include <string.h>


#include <assert.h>
#include <ctype.h>


#include "rtmp_sys.h"
#include "log.h"




/**

 * @brief解析URL,得到协议类型、主机名称、端口号、播放路径和长度、应用程序名称和长度。

除了url后面的都是输出型参数

 */


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;


/* 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);


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)    //如果存在:,也就是url中有指定端口号,那么上面求得的长度实际上包含了:端口,需要更正
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;
}


/* get the port number if available */
if(*p == ':') {
unsigned int p2;
p++;
p2 = atoi(p);
if(p2 > 65535) {//65535是规定的最大端口号
RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
} else {
*port = p2;
}
}


if(!slash) {                 //如果不存在斜杆,证明不存在应用名即,url的形式为 rtmp://192.168.1.2,立即返回
RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
return TRUE;
}
p = slash+1;  //跳过斜杆,指向了应用名


{
/* parse application
*
* rtmp://host[:port]/app[/appinstance][/...]
* application = app[/appinstance]
*/

//实际的url可能为:rtmp://host:1935/app_name/stream_name?键1=键值&键2=键值,app_name可能是一个xxx/xxx/xxxx的形式
char *slash2, *slash3 = NULL, *slash4 = NULL;
int applen, appnamelen;


slash2 = strchr(p, '/');//查找应用名后面的斜杆,slash3和slash4一般是空的
if(slash2)
slash3 = strchr(slash2+1, '/');
if(slash3)
slash4 = strchr(slash3+1, '/');


applen = end-p; /* ondemand, pass all parameters as app */
appnamelen = applen; /* ondemand length */

//判断url是否带有参数,即?后面的部分
if(ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse playpath 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(slash4)
appnamelen = slash4-p;
else 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);//应用名后面的部分交给RTMP_ParsePlaypath去解析
}


return TRUE;
}


/*
 * Extracts playpath from RTMP URL. playpath is the file part of the
 * URL, i.e. the part that comes after rtmp://host:port/app/

 * 从url中提取出播放路径,播放路径是指rtmp://host:port/app/后面的部分

返回FMS能理解的格式的流名字

 * Returns the stream name in a format understood by FMS. The name is
 * the playpath part of the URL with formatting depending on the stream

 * type:

 对于hong.mp4,返回mp4:hong

 * mp4 streams: prepend "mp4:", remove extension
 * mp3 streams: prepend "mp3:", remove extension
 * flv streams: remove extension
 */
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;          //跳过slist=
pplen = strlen(ppstart);


temp = strchr(ppstart, '&');//更正长度,因为slist=路径后面可能还有其他参数
if (temp) {
pplen = temp-ppstart;
}
}

//对于只含有?而不含有slist=的情况
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;
}
}

//4用于存放后缀,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;
}

猜你喜欢

转载自blog.csdn.net/handsomehong/article/details/79047639