parseurl.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /*
  2. * Copyright (C) 2009 Andrej Stepanchuk
  3. * Copyright (C) 2009-2010 Howard Chu
  4. *
  5. * This file is part of librtmp.
  6. *
  7. * librtmp is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1,
  10. * or (at your option) any later version.
  11. *
  12. * librtmp is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with librtmp see the file COPYING. If not, write to
  19. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  20. * Boston, MA 02110-1301, USA.
  21. * http://www.gnu.org/copyleft/lgpl.html
  22. */
  23. #include "rtmp_sys.h"
  24. #include "log.h"
  25. int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
  26. AVal *playpath, AVal *app)
  27. {
  28. char *p, *end, *col, *ques, *slash;
  29. RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
  30. *protocol = RTMP_PROTOCOL_RTMP;
  31. *port = 0;
  32. playpath->av_len = 0;
  33. playpath->av_val = NULL;
  34. app->av_len = 0;
  35. app->av_val = NULL;
  36. /* Old School Parsing */
  37. /* look for usual :// pattern */
  38. p = strstr(url, "://");
  39. if(!p)
  40. {
  41. RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
  42. return FALSE;
  43. }
  44. {
  45. int len = (int)(p-url);
  46. if(len == 4 && strncasecmp(url, "rtmp", 4)==0)
  47. *protocol = RTMP_PROTOCOL_RTMP;
  48. else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0)
  49. *protocol = RTMP_PROTOCOL_RTMPT;
  50. else if(len == 5 && strncasecmp(url, "rtmps", 5)==0)
  51. *protocol = RTMP_PROTOCOL_RTMPS;
  52. else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0)
  53. *protocol = RTMP_PROTOCOL_RTMPE;
  54. else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0)
  55. *protocol = RTMP_PROTOCOL_RTMFP;
  56. else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0)
  57. *protocol = RTMP_PROTOCOL_RTMPTE;
  58. else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0)
  59. *protocol = RTMP_PROTOCOL_RTMPTS;
  60. else
  61. {
  62. RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
  63. goto parsehost;
  64. }
  65. }
  66. RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
  67. parsehost:
  68. /* let's get the hostname */
  69. p+=3;
  70. /* check for sudden death */
  71. if(*p==0)
  72. {
  73. RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
  74. return FALSE;
  75. }
  76. end = p + strlen(p);
  77. col = strchr(p, ':');
  78. ques = strchr(p, '?');
  79. slash = strchr(p, '/');
  80. {
  81. int hostlen;
  82. if(slash)
  83. hostlen = slash - p;
  84. else
  85. hostlen = end - p;
  86. if(col && col -p < hostlen)
  87. hostlen = col - p;
  88. if(hostlen < 256)
  89. {
  90. host->av_val = p;
  91. host->av_len = hostlen;
  92. RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
  93. }
  94. else
  95. {
  96. RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
  97. }
  98. p+=hostlen;
  99. }
  100. /* get the port number if available */
  101. if(*p == ':')
  102. {
  103. unsigned int p2;
  104. p++;
  105. p2 = atoi(p);
  106. if(p2 > 65535)
  107. {
  108. RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
  109. }
  110. else
  111. {
  112. *port = p2;
  113. }
  114. }
  115. if(!slash)
  116. {
  117. RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
  118. return TRUE;
  119. }
  120. p = slash+1;
  121. {
  122. /* parse application
  123. *
  124. * rtmp://host[:port]/app[/appinstance][/...]
  125. * application = app[/appinstance]
  126. */
  127. char *slash2, *slash3 = NULL, *slash4 = NULL;
  128. int applen, appnamelen;
  129. slash2 = strchr(p, '/');
  130. if(slash2)
  131. slash3 = strchr(slash2+1, '/');
  132. if(slash3)
  133. slash4 = strchr(slash3+1, '/');
  134. applen = end-p; /* ondemand, pass all parameters as app */
  135. appnamelen = applen; /* ondemand length */
  136. if(ques && strstr(p, "slist=")) /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
  137. {
  138. appnamelen = ques-p;
  139. }
  140. else if(strncmp(p, "ondemand/", 9)==0)
  141. {
  142. /* app = ondemand/foobar, only pass app=ondemand */
  143. applen = 8;
  144. appnamelen = 8;
  145. }
  146. else /* app!=ondemand, so app is app[/appinstance] */
  147. {
  148. if(slash4)
  149. appnamelen = slash4-p;
  150. else if(slash3)
  151. appnamelen = slash3-p;
  152. else if(slash2)
  153. appnamelen = slash2-p;
  154. applen = appnamelen;
  155. }
  156. app->av_val = p;
  157. app->av_len = applen;
  158. RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
  159. p += appnamelen;
  160. }
  161. if (*p == '/')
  162. p++;
  163. if (end-p)
  164. {
  165. AVal av = {p, end-p};
  166. RTMP_ParsePlaypath(&av, playpath);
  167. }
  168. return TRUE;
  169. }
  170. int RTMP_ParseURL2(const char *url, int *protocol, AVal *host, unsigned int *port,
  171. AVal *app)
  172. {
  173. char *p, *end, *col, /* *ques, */ *slash;
  174. RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
  175. *protocol = RTMP_PROTOCOL_RTMP;
  176. *port = 0;
  177. app->av_len = 0;
  178. app->av_val = NULL;
  179. /* Old School Parsing */
  180. /* look for usual :// pattern */
  181. p = strstr(url, "://");
  182. if(!p)
  183. {
  184. RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
  185. return FALSE;
  186. }
  187. {
  188. int len = (int)(p-url);
  189. if(len == 4 && strncasecmp(url, "rtmp", 4)==0)
  190. *protocol = RTMP_PROTOCOL_RTMP;
  191. else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0)
  192. *protocol = RTMP_PROTOCOL_RTMPT;
  193. else if(len == 5 && strncasecmp(url, "rtmps", 5)==0)
  194. *protocol = RTMP_PROTOCOL_RTMPS;
  195. else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0)
  196. *protocol = RTMP_PROTOCOL_RTMPE;
  197. else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0)
  198. *protocol = RTMP_PROTOCOL_RTMFP;
  199. else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0)
  200. *protocol = RTMP_PROTOCOL_RTMPTE;
  201. else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0)
  202. *protocol = RTMP_PROTOCOL_RTMPTS;
  203. else
  204. {
  205. RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
  206. goto parsehost;
  207. }
  208. }
  209. RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
  210. parsehost:
  211. /* let's get the hostname */
  212. p+=3;
  213. /* check for sudden death */
  214. if(*p==0)
  215. {
  216. RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
  217. return FALSE;
  218. }
  219. end = p + strlen(p);
  220. col = strchr(p, ':');
  221. // ques = strchr(p, '?');
  222. slash = strchr(p, '/');
  223. {
  224. int hostlen;
  225. if(slash)
  226. hostlen = slash - p;
  227. else
  228. hostlen = end - p;
  229. if(col && col -p < hostlen)
  230. hostlen = col - p;
  231. if(hostlen < 256)
  232. {
  233. host->av_val = p;
  234. host->av_len = hostlen;
  235. RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
  236. }
  237. else
  238. {
  239. RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
  240. }
  241. p+=hostlen;
  242. }
  243. /* get the port number if available */
  244. if(*p == ':')
  245. {
  246. unsigned int p2;
  247. p++;
  248. p2 = atoi(p);
  249. if(p2 > 65535)
  250. {
  251. RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
  252. }
  253. else
  254. {
  255. *port = p2;
  256. }
  257. }
  258. if(!slash)
  259. {
  260. RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
  261. return TRUE;
  262. }
  263. p = slash+1;
  264. //just.. whatever.
  265. app->av_val = p;
  266. app->av_len = (int)strlen(p);
  267. if(app->av_len && p[app->av_len-1] == '/')
  268. app->av_len--;
  269. RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", app->av_len, p);
  270. p += app->av_len;
  271. if (*p == '/')
  272. p++;
  273. return TRUE;
  274. }
  275. /*
  276. * Extracts playpath from RTMP URL. playpath is the file part of the
  277. * URL, i.e. the part that comes after rtmp://host:port/app/
  278. *
  279. * Returns the stream name in a format understood by FMS. The name is
  280. * the playpath part of the URL with formatting depending on the stream
  281. * type:
  282. *
  283. * mp4 streams: prepend "mp4:", remove extension
  284. * mp3 streams: prepend "mp3:", remove extension
  285. * flv streams: remove extension
  286. */
  287. void RTMP_ParsePlaypath(AVal *in, AVal *out)
  288. {
  289. int addMP4 = 0;
  290. int addMP3 = 0;
  291. int subExt = 0;
  292. const char *playpath = in->av_val;
  293. const char *temp, *q, *ext = NULL;
  294. const char *ppstart = playpath;
  295. char *streamname, *destptr, *p;
  296. int pplen = in->av_len;
  297. out->av_val = NULL;
  298. out->av_len = 0;
  299. if ((*ppstart == '?') &&
  300. (temp=strstr(ppstart, "slist=")) != 0)
  301. {
  302. ppstart = temp+6;
  303. pplen = (int)strlen(ppstart);
  304. temp = strchr(ppstart, '&');
  305. if (temp)
  306. {
  307. pplen = temp-ppstart;
  308. }
  309. }
  310. q = strchr(ppstart, '?');
  311. if (pplen >= 4)
  312. {
  313. if (q)
  314. ext = q-4;
  315. else
  316. ext = &ppstart[pplen-4];
  317. if ((strncmp(ext, ".f4v", 4) == 0) ||
  318. (strncmp(ext, ".mp4", 4) == 0))
  319. {
  320. addMP4 = 1;
  321. subExt = 1;
  322. /* Only remove .flv from rtmp URL, not slist params */
  323. }
  324. else if ((ppstart == playpath) &&
  325. (strncmp(ext, ".flv", 4) == 0))
  326. {
  327. subExt = 1;
  328. }
  329. else if (strncmp(ext, ".mp3", 4) == 0)
  330. {
  331. addMP3 = 1;
  332. subExt = 1;
  333. }
  334. }
  335. streamname = (char *)malloc((pplen+4+1)*sizeof(char));
  336. if (!streamname)
  337. return;
  338. destptr = streamname;
  339. if (addMP4)
  340. {
  341. if (strncmp(ppstart, "mp4:", 4))
  342. {
  343. strcpy(destptr, "mp4:");
  344. destptr += 4;
  345. }
  346. else
  347. {
  348. subExt = 0;
  349. }
  350. }
  351. else if (addMP3)
  352. {
  353. if (strncmp(ppstart, "mp3:", 4))
  354. {
  355. strcpy(destptr, "mp3:");
  356. destptr += 4;
  357. }
  358. else
  359. {
  360. subExt = 0;
  361. }
  362. }
  363. for (p=(char *)ppstart; pplen >0;)
  364. {
  365. /* skip extension */
  366. if (subExt && p == ext)
  367. {
  368. p += 4;
  369. pplen -= 4;
  370. continue;
  371. }
  372. if (*p == '%')
  373. {
  374. unsigned int c;
  375. sscanf(p+1, "%02x", &c);
  376. *destptr++ = c;
  377. pplen -= 3;
  378. p += 3;
  379. }
  380. else
  381. {
  382. *destptr++ = *p++;
  383. pplen--;
  384. }
  385. }
  386. *destptr = '\0';
  387. out->av_val = streamname;
  388. out->av_len = destptr - streamname;
  389. }