• RTMPDump(libRTMP)源代码分析 2:解析RTMP地址——RTMP_ParseURL()


    注:此前写了一些列的分析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!
    ");
    		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 = '';
    
    	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


  • 相关阅读:
    Django学习-9-ORM多对多操作
    Django学习-8-模板渲染的一些特性
    Django学习-7-ORM操作
    Django学习-6-路由系统
    Hadoop 知识
    最全的Spark基础知识解答
    windows server 2008 R2 远程连接用户数修改
    Windows 2008 R2 SP1部署WSUS 3.0 SP2
    Windows Server2008 R2 设置NAT 让Hyper-V连接Internet
    免费好用的Microsoft iSCSI Software Target 3.3
  • 原文地址:https://www.cnblogs.com/leixiaohua1020/p/3902030.html
Copyright © 2020-2023  润新知