LibRTMP源代码分析2

获取RTMP流媒体数据很重要的前提是RTMP的URL的解析,首先回顾一下RTMP的URL的格式:
         url = “rtmp://pub1.guoshi.com:1935/pushstation/ 291?wsSecret=2b8 &wsTime=5302
         协议名 :RTMP_PROTOCOL_RTMP = 0;
         主机名 :"pub1.guoshi.com";
         端口号 :1935; (可以没有)
         App名  : "pushstation";
         播放路径:"291?wsSecret=2b8 &wsTime=5302";

/**
 * @brief解析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 */
        // 查找“://”
        // 函数原型 :char *strstr(char *str1, char *str2); 
        // 功能 :找出str2字符串在str1字符串中第一次出现的位置 
        // 返回值 :返回该位置的指针,如找不到,返回空指针。
p = strstr(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;

/* check for sudden death,检查主机名 */
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); // 指向url字符串末尾 
col = strchr(p, ':'); // 指向"://"之后的第一个":" 
ques = strchr(p, '?'); // 指向"://"之后的第一个"?" 
slash = strchr(p, '/'); // 指向"://"之后的第一个"/"

 { 
// 获取主机名字符串长度,也就是"://"与下一个"/"之间的距离 
int hostlen; 
if (slash) 
hostlen = slash - p; 
else 
hostlen = end - p; 

// 如果":"在"/"之前出现,在主机名长度就算到":"为止。 
// 例如 :rtmp://wavelet:1935/vod 
// 这个时候主机名为wavelet,而不是wavelet:1935,端口号为1935
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; 
}

// 如果主机名后不存在"/",则表示URL中没有应用程序名或播放文件路径,直接返回
if (!slash) 
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] 
  */ 
char *slash2, *slash3 = NULL; // 分别指向第二个和第三个斜杠 
int applen, appnamelen; 
// 举例(山东卫视): 
// rtmp://112.231.23.20:554/rtplive/d332e5ce23a3b6ectv01 
// 存在slash2,但是不存在slash3 
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) 
// 举例: rtmp://flashserver:1935/ondemand/thefile swfUrl=http://flashserver/player.swfswfVfy=1 
/* 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;
}


/** 
 * @brief 从URL中获取播放路径(playpath)。
 *  播放路径就是URL中“rtmp://host:port/app/”后面的部分。
 * 
 *  获取FMS能够识别的播放路径 
 *  mp4 流: 前面添加 "mp4:", 删除扩展名 
 *  mp3 流: 前面添加 "mp3:", 删除扩展名 
 *  flv 流: 删除扩展名 
 */  
void RTMP_ParsePlaypath(AVal *in, AVal *out);

猜你喜欢

转载自blog.csdn.net/wongainia158158/article/details/48625123