码迷,mamicode.com
首页 > 其他好文 > 详细

转:RTMPDump源代码分析

时间:2015-05-21 23:55:27      阅读:414      评论:0      收藏:0      [点我收藏+]

标签:

0: 主要函数调用分析

rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://.也提供 Android 版本。

最近研究了一下它内部函数调用的关系。

下面列出几个主要的函数的调用关系。

RTMPDump用于下载RTMP流媒体的函数Download:

技术分享

用于建立网络连接(NetConnect)的函数Connect:

技术分享

用于建立网络流(NetStream)的函数

技术分享

rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561

rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163

1: main()函数

rtmpdump 是一个用来处理 RTMP 流媒体的工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps:// 等。之前在学习RTMP协议的时候,发现没有讲它源代码的,只好自己分析,现在打算把自己学习的成果写出来,可能结果不一定都对,先暂且记录一下。

使用RTMPdump下载一个流媒体的大致流程是这样的:

[cpp] view plaincopy

  1. RTMP_Init();//初始化结构体
  2. InitSockets();//初始化Socket
  3. RTMP_ParseURL();//解析输入URL
  4. RTMP_SetupStream();//一些设置
  5. fopen();//打开文件,准备写入
  6. RTMP_Connect();//建立NetConnection
  7. RTMP_ConnectStream()//建立NetStream
  8. Download();//下载函数
  9. RTMP_Close();//关闭连接
  10. fclose();//关闭文件
  11. CleanupSockets();//清理Socket

其中Download()主要是使用RTMP_Read()进行下载的。

注:可以参考:RTMP流媒体播放过程

下面贴上自己注释的RTMPDump源代码。注意以下几点:

1.此RTMPDump已经被移植进VC 2010 的 MFC的工程,所以main()函数已经被改名为rtmpdump(),而且参数也改了,传进来一个MFC窗口的句柄。不过功能没怎么改(控制台程序移植到MFC以后,main()就不是程序的入口了,所以main()名字改成什么是无所谓的)

2.里面有很多提取信息的代码形如:rtmp.dlg->AppendCInfo("开始初始化Socket...");这些代码是我为了获取RTMP信息而自己加的,并不影响程序的执行。

[cpp] view plaincopy

  1. int rtmpdump(LPVOID lpParam,int argc,char **argv) 
  2. extern char *optarg; 
  3. //一定要设置,否则只能运行一次
  4. extern int optind; 
  5.   optind=0; 
  6. int nStatus = RD_SUCCESS; 
  7. double percent = 0; 
  8. double duration = 0.0; 
  9. int nSkipKeyFrames = DEF_SKIPFRM; // skip this number of keyframes when resuming
  10. int bOverrideBufferTime = FALSE;  // if the user specifies a buffer time override this is true
  11. int bStdoutMode = TRUE;   // if true print the stream directly to stdout, messages go to stderr
  12. int bResume = FALSE;      // true in resume mode
  13.   uint32_t dSeek = 0;       // seek position in resume mode, 0 otherwise
  14.   uint32_t bufferTime = DEF_BUFTIME; 
  15. // meta header and initial frame for the resume mode (they are read from the file and compared with
  16. // the stream we are trying to continue
  17. char *metaHeader = 0; 
  18.   uint32_t nMetaHeaderSize = 0; 
  19. // video keyframe for matching
  20. char *initialFrame = 0; 
  21.   uint32_t nInitialFrameSize = 0; 
  22. int initialFrameType = 0; // tye: audio or video
  23.   AVal hostname = { 0, 0 }; 
  24.   AVal playpath = { 0, 0 }; 
  25.   AVal subscribepath = { 0, 0 }; 
  26. int port = -1; 
  27. int protocol = RTMP_PROTOCOL_UNDEFINED; 
  28. int retries = 0; 
  29. int bLiveStream = FALSE;  // 是直播流吗? then we can‘t seek/resume
  30. int bHashes = FALSE;      // display byte counters not hashes by default
  31. long int timeout = DEF_TIMEOUT;   // timeout connection after 120 seconds
  32.   uint32_t dStartOffset = 0;    // 非直播流搜寻点seek position in non-live mode
  33.   uint32_t dStopOffset = 0; 
  34.   RTMP rtmp = { 0 }; 
  35.   AVal swfUrl = { 0, 0 }; 
  36.   AVal tcUrl = { 0, 0 }; 
  37.   AVal pageUrl = { 0, 0 }; 
  38.   AVal app = { 0, 0 }; 
  39.   AVal auth = { 0, 0 }; 
  40.   AVal swfHash = { 0, 0 }; 
  41.   uint32_t swfSize = 0; 
  42.   AVal flashVer = { 0, 0 }; 
  43.   AVal sockshost = { 0, 0 }; 
  44. #ifdef CRYPTO
  45. int swfAge = 30;  /* 30 days for SWF cache by default */
  46. int swfVfy = 0; 
  47.   unsigned char hash[RTMP_SWF_HASHLEN]; 
  48. #endif
  49. char *flvFile = 0; 
  50.   signal(SIGINT, sigIntHandler); 
  51.   signal(SIGTERM, sigIntHandler); 
  52. #ifndef WIN32
  53.   signal(SIGHUP, sigIntHandler); 
  54.   signal(SIGPIPE, sigIntHandler); 
  55.   signal(SIGQUIT, sigIntHandler); 
  56. #endif
  57.   RTMP_debuglevel = RTMP_LOGINFO; 
  58. //首先搜寻“ --quiet”选项
  59. int index = 0; 
  60. while (index < argc) 
  61.     { 
  62. if (strcmp(argv[index], "--quiet") == 0 
  63.       || strcmp(argv[index], "-q") == 0) 
  64.     RTMP_debuglevel = RTMP_LOGCRIT; 
  65.       index++; 
  66.     } 
  67. #define RTMPDUMP_VERSION "1.0"
  68.   RTMP_LogPrintf("RTMP流媒体下载 %s\n", RTMPDUMP_VERSION); 
  69.   RTMP_LogPrintf 
  70.     ("2012 雷霄骅 中国传媒大学/信息工程学院/通信与信息系统/数字电视技术\n"); 
  71. //RTMP_LogPrintf("输入 -h 获取命令选项\n");
  72.     RTMP_Init(&rtmp); 
  73. //句柄-----------------------------
  74.     rtmp.dlg=(CSpecialPRTMPDlg *)lpParam; 
  75. //---------------------------------
  76. //----------------------
  77.     rtmp.dlg->AppendCInfo("开始初始化Socket..."); 
  78. //-----------------------------
  79. if (!InitSockets()) 
  80.     { 
  81. //----------------------
  82.         rtmp.dlg->AppendCInfo("初始化Socket失败!"); 
  83. //-----------------------------
  84.         RTMP_Log(RTMP_LOGERROR, 
  85. "Couldn‘t load sockets support on your platform, exiting!"); 
  86. return RD_FAILED; 
  87.     } 
  88. //----------------------
  89.   rtmp.dlg->AppendCInfo("成功初始化Socket"); 
  90. //-----------------------------
  91. /* sleep(30); */
  92. int opt; 
  93. /*  struct option longopts[] = {
  94.     {"help", 0, NULL, ‘h‘},
  95.     {"host", 1, NULL, ‘n‘},
  96.     {"port", 1, NULL, ‘c‘},
  97.     {"socks", 1, NULL, ‘S‘},
  98.     {"protocol", 1, NULL, ‘l‘},
  99.     {"playpath", 1, NULL, ‘y‘},
  100.     {"playlist", 0, NULL, ‘Y‘},
  101.     {"rtmp", 1, NULL, ‘r‘},
  102.     {"swfUrl", 1, NULL, ‘s‘},
  103.     {"tcUrl", 1, NULL, ‘t‘},
  104.     {"pageUrl", 1, NULL, ‘p‘},
  105.     {"app", 1, NULL, ‘a‘},
  106.     {"auth", 1, NULL, ‘u‘},
  107.     {"conn", 1, NULL, ‘C‘},
  108. #ifdef CRYPTO
  109.     {"swfhash", 1, NULL, ‘w‘},
  110.     {"swfsize", 1, NULL, ‘x‘},
  111.     {"swfVfy", 1, NULL, ‘W‘},
  112.     {"swfAge", 1, NULL, ‘X‘},
  113. #endif
  114.     {"flashVer", 1, NULL, ‘f‘},
  115.     {"live", 0, NULL, ‘v‘},
  116.     {"flv", 1, NULL, ‘o‘},
  117.     {"resume", 0, NULL, ‘e‘},
  118.     {"timeout", 1, NULL, ‘m‘},
  119.     {"buffer", 1, NULL, ‘b‘},
  120.     {"skip", 1, NULL, ‘k‘},
  121.     {"subscribe", 1, NULL, ‘d‘},
  122.     {"start", 1, NULL, ‘A‘},
  123.     {"stop", 1, NULL, ‘B‘},
  124.     {"token", 1, NULL, ‘T‘},
  125.     {"hashes", 0, NULL, ‘#‘},
  126.     {"debug", 0, NULL, ‘z‘},
  127.     {"quiet", 0, NULL, ‘q‘},
  128.     {"verbose", 0, NULL, ‘V‘},
  129.     {0, 0, 0, 0}
  130.   };*/
  131. //分析命令行参数,注意用法。
  132. //选项都是一个字母,后面有冒号的代表该选项还有相关参数
  133. //一直循环直到获取所有的opt
  134. while ((opt = 
  135.       getopt/*_long*/(argc, argv, 
  136. "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#"/*,
  137.               longopts, NULL*/)) != -1) 
  138.     { 
  139. //不同的选项做不同的处理
  140. switch (opt) 
  141.     { 
  142. case ‘h‘: 
  143.       usage(argv[0]); 
  144. return RD_SUCCESS; 
  145. #ifdef CRYPTO
  146. case ‘w‘: 
  147.       { 
  148. int res = hex2bin(optarg, &swfHash.av_val); 
  149. if (res != RTMP_SWF_HASHLEN) 
  150.           { 
  151.         swfHash.av_val = NULL; 
  152.         RTMP_Log(RTMP_LOGWARNING, 
  153. "Couldn‘t parse swf hash hex string, not hexstring or not %d bytes, ignoring!", RTMP_SWF_HASHLEN); 
  154.           } 
  155.         swfHash.av_len = RTMP_SWF_HASHLEN; 
  156. break; 
  157.       } 
  158. case ‘x‘: 
  159.       { 
  160. int size = atoi(optarg); 
  161. if (size <= 0) 
  162.           { 
  163.         RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n"); 
  164.           } 
  165. else
  166.           { 
  167.         swfSize = size; 
  168.           } 
  169. break; 
  170.       } 
  171. case ‘W‘: 
  172.       STR2AVAL(swfUrl, optarg); 
  173.       swfVfy = 1; 
  174. break; 
  175. case ‘X‘: 
  176.       { 
  177. int num = atoi(optarg); 
  178. if (num < 0) 
  179.           { 
  180.         RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n"); 
  181.           } 
  182. else
  183.           { 
  184.         swfAge = num; 
  185.           } 
  186.       } 
  187. break; 
  188. #endif
  189. case ‘k‘: 
  190.       nSkipKeyFrames = atoi(optarg); 
  191. if (nSkipKeyFrames < 0) 
  192.         { 
  193.           RTMP_Log(RTMP_LOGERROR, 
  194. "Number of keyframes skipped must be greater or equal zero, using zero!"); 
  195.           nSkipKeyFrames = 0; 
  196.         } 
  197. else
  198.         { 
  199.           RTMP_Log(RTMP_LOGDEBUG, "Number of skipped key frames for resume: %d", 
  200.           nSkipKeyFrames); 
  201.         } 
  202. break; 
  203. case ‘b‘: 
  204.       { 
  205.         int32_t bt = atol(optarg); 
  206. if (bt < 0) 
  207.           { 
  208.         RTMP_Log(RTMP_LOGERROR, 
  209. "Buffer time must be greater than zero, ignoring the specified value %d!", 
  210.             bt); 
  211.           } 
  212. else
  213.           { 
  214.         bufferTime = bt; 
  215.         bOverrideBufferTime = TRUE; 
  216.           } 
  217. break; 
  218.       } 
  219. //直播流
  220. case ‘v‘: 
  221. //----------------
  222.         rtmp.dlg->AppendCInfo("该RTMP的URL是一个直播流"); 
  223. //----------------
  224.       bLiveStream = TRUE;   // no seeking or resuming possible!
  225. break; 
  226. case ‘d‘: 
  227.       STR2AVAL(subscribepath, optarg); 
  228. break; 
  229. case ‘n‘: 
  230.       STR2AVAL(hostname, optarg); 
  231. break; 
  232. case ‘c‘: 
  233.       port = atoi(optarg); 
  234. break; 
  235. case ‘l‘: 
  236.       protocol = atoi(optarg); 
  237. if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS) 
  238.         { 
  239.           RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol); 
  240. return RD_FAILED; 
  241.         } 
  242. break; 
  243. case ‘y‘: 
  244.       STR2AVAL(playpath, optarg); 
  245. break; 
  246. case ‘Y‘: 
  247.       RTMP_SetOpt(&rtmp, &av_playlist, (AVal *)&av_true); 
  248. break; 
  249. //路径参数-r
  250. case ‘r‘: 
  251.       { 
  252.         AVal parsedHost, parsedApp, parsedPlaypath; 
  253.         unsigned int parsedPort = 0; 
  254. int parsedProtocol = RTMP_PROTOCOL_UNDEFINED; 
  255. //解析URL。注optarg指向参数(URL)
  256.         RTMP_LogPrintf("RTMP URL : %s\n",optarg); 
  257. //----------------
  258.         rtmp.dlg->AppendCInfo("解析RTMP的URL..."); 
  259. //----------------
  260. if (!RTMP_ParseURL 
  261.         (optarg, &parsedProtocol, &parsedHost, &parsedPort, 
  262.          &parsedPlaypath, &parsedApp)) 
  263.           { 
  264. //----------------
  265.             rtmp.dlg->AppendCInfo("解析RTMP的URL失败!"); 
  266. //----------------
  267.         RTMP_Log(RTMP_LOGWARNING, "无法解析 url (%s)!", 
  268.             optarg); 
  269.           } 
  270. else
  271.           { 
  272. //----------------
  273.             rtmp.dlg->AppendCInfo("解析RTMP的URL成功"); 
  274. //----------------
  275. //把解析出来的数据赋值
  276. if (!hostname.av_len) 
  277.           hostname = parsedHost; 
  278. if (port == -1) 
  279.           port = parsedPort; 
  280. if (playpath.av_len == 0 && parsedPlaypath.av_len) 
  281.           { 
  282.             playpath = parsedPlaypath; 
  283.           } 
  284. if (protocol == RTMP_PROTOCOL_UNDEFINED) 
  285.           protocol = parsedProtocol; 
  286. if (app.av_len == 0 && parsedApp.av_len) 
  287.           { 
  288.             app = parsedApp; 
  289.           } 
  290.           } 
  291. break; 
  292.       } 
  293. case ‘s‘: 
  294.       STR2AVAL(swfUrl, optarg); 
  295. break; 
  296. case ‘t‘: 
  297.       STR2AVAL(tcUrl, optarg); 
  298. break; 
  299. case ‘p‘: 
  300.       STR2AVAL(pageUrl, optarg); 
  301. break; 
  302. case ‘a‘: 
  303.       STR2AVAL(app, optarg); 
  304. break; 
  305. case ‘f‘: 
  306.       STR2AVAL(flashVer, optarg); 
  307. break; 
  308. //指定输出文件
  309. case ‘o‘: 
  310.       flvFile = optarg; 
  311. if (strcmp(flvFile, "-")) 
  312.         bStdoutMode = FALSE; 
  313. break; 
  314. case ‘e‘: 
  315.       bResume = TRUE; 
  316. break; 
  317. case ‘u‘: 
  318.       STR2AVAL(auth, optarg); 
  319. break; 
  320. case ‘C‘: { 
  321.       AVal av; 
  322.       STR2AVAL(av, optarg); 
  323. if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) 
  324.         { 
  325.           RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg); 
  326. return RD_FAILED; 
  327.         } 
  328.       } 
  329. break; 
  330. case ‘m‘: 
  331.       timeout = atoi(optarg); 
  332. break; 
  333. case ‘A‘: 
  334.       dStartOffset = (int) (atof(optarg) * 1000.0); 
  335. break; 
  336. case ‘B‘: 
  337.       dStopOffset = (int) (atof(optarg) * 1000.0); 
  338. break; 
  339. case ‘T‘: { 
  340.       AVal token; 
  341.       STR2AVAL(token, optarg); 
  342.       RTMP_SetOpt(&rtmp, &av_token, &token); 
  343.       } 
  344. break; 
  345. case ‘#‘: 
  346.       bHashes = TRUE; 
  347. break; 
  348. case ‘q‘: 
  349.       RTMP_debuglevel = RTMP_LOGCRIT; 
  350. break; 
  351. case ‘V‘: 
  352.       RTMP_debuglevel = RTMP_LOGDEBUG; 
  353. break; 
  354. case ‘z‘: 
  355.       RTMP_debuglevel = RTMP_LOGALL; 
  356. break; 
  357. case ‘S‘: 
  358.       STR2AVAL(sockshost, optarg); 
  359. break; 
  360. default: 
  361.       RTMP_LogPrintf("unknown option: %c\n", opt); 
  362.       usage(argv[0]); 
  363. return RD_FAILED; 
  364. break; 
  365.     } 
  366.     } 
  367. if (!hostname.av_len) 
  368.     { 
  369.       RTMP_Log(RTMP_LOGERROR, 
  370. "您必须指定 主机名(hostname) (--host) 或 url (-r \"rtmp://host[:port]/playpath\") 包含 a hostname"); 
  371. return RD_FAILED; 
  372.     } 
  373. if (playpath.av_len == 0) 
  374.     { 
  375.       RTMP_Log(RTMP_LOGERROR, 
  376. "您必须指定 播放路径(playpath) (--playpath) 或 url (-r \"rtmp://host[:port]/playpath\") 包含 a playpath"); 
  377. return RD_FAILED; 
  378.     } 
  379. if (protocol == RTMP_PROTOCOL_UNDEFINED) 
  380.     { 
  381.       RTMP_Log(RTMP_LOGWARNING, 
  382. "您没有指定 协议(protocol) (--protocol) 或 rtmp url (-r), 默认协议 RTMP"); 
  383.       protocol = RTMP_PROTOCOL_RTMP; 
  384.     } 
  385. if (port == -1) 
  386.     { 
  387.       RTMP_Log(RTMP_LOGWARNING, 
  388. "您没有指定 端口(port) (--port) 或 rtmp url (-r), 默认端口 1935"); 
  389.       port = 0; 
  390.     } 
  391. if (port == 0) 
  392.     { 
  393. if (protocol & RTMP_FEATURE_SSL) 
  394.     port = 443; 
  395. else if (protocol & RTMP_FEATURE_HTTP) 
  396.     port = 80; 
  397. else
  398.     port = 1935; 
  399.     } 
  400. if (flvFile == 0) 
  401.     { 
  402.       RTMP_Log(RTMP_LOGWARNING, 
  403. "请指定一个输出文件 (-o filename), using stdout"); 
  404.       bStdoutMode = TRUE; 
  405.     } 
  406. if (bStdoutMode && bResume) 
  407.     { 
  408.       RTMP_Log(RTMP_LOGWARNING, 
  409. "Can‘t resume in stdout mode, ignoring --resume option"); 
  410.       bResume = FALSE; 
  411.     } 
  412. if (bLiveStream && bResume) 
  413.     { 
  414.       RTMP_Log(RTMP_LOGWARNING, "Can‘t resume live stream, ignoring --resume option"); 
  415.       bResume = FALSE; 
  416.     } 
  417. #ifdef CRYPTO
  418. if (swfVfy) 
  419.     { 
  420. if (RTMP_HashSWF(swfUrl.av_val, (unsigned int *)&swfSize, hash, swfAge) == 0) 
  421.         { 
  422.           swfHash.av_val = (char *)hash; 
  423.           swfHash.av_len = RTMP_SWF_HASHLEN; 
  424.         } 
  425.     } 
  426. if (swfHash.av_len == 0 && swfSize > 0) 
  427.     { 
  428.       RTMP_Log(RTMP_LOGWARNING, 
  429. "Ignoring SWF size, supply also the hash with --swfhash"); 
  430.       swfSize = 0; 
  431.     } 
  432. if (swfHash.av_len != 0 && swfSize == 0) 
  433.     { 
  434.       RTMP_Log(RTMP_LOGWARNING, 
  435. "Ignoring SWF hash, supply also the swf size  with --swfsize"); 
  436.       swfHash.av_len = 0; 
  437.       swfHash.av_val = NULL; 
  438.     } 
  439. #endif
  440. if (tcUrl.av_len == 0) 
  441.     { 
  442. char str[512] = { 0 }; 
  443.       tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s", 
  444.            RTMPProtocolStringsLower[protocol], hostname.av_len, 
  445.            hostname.av_val, port, app.av_len, app.av_val); 
  446.       tcUrl.av_val = (char *) malloc(tcUrl.av_len + 1); 
  447.       strcpy(tcUrl.av_val, str); 
  448.     } 
  449. int first = 1; 
  450. // User defined seek offset
  451. if (dStartOffset > 0) 
  452.     { 
  453. //直播流
  454. if (bLiveStream) 
  455.     { 
  456.       RTMP_Log(RTMP_LOGWARNING, 
  457. "Can‘t seek in a live stream, ignoring --start option"); 
  458.       dStartOffset = 0; 
  459.     } 
  460.     } 
  461. //----------------
  462.   rtmp.dlg->AppendCInfo("开始初始化RTMP连接的参数..."); 
  463. //----------------
  464. //设置
  465.   RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, 
  466.            &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, 
  467.            &flashVer, &subscribepath, dSeek, dStopOffset, bLiveStream, timeout); 
  468. //此处设置参数-----------------
  469.   rtmp.dlg->AppendCInfo("成功初始化RTMP连接的参数"); 
  470. //-----------------------------
  471. char *temp=(char *)malloc(MAX_URL_LENGTH); 
  472.   memcpy(temp,rtmp.Link.hostname.av_val,rtmp.Link.hostname.av_len); 
  473.   temp[rtmp.Link.hostname.av_len]=‘\0‘; 
  474.   rtmp.dlg->AppendB_R_L_Info("主机名",temp); 
  475.   itoa(rtmp.Link.port,temp,10); 
  476.   rtmp.dlg->AppendB_R_L_Info("端口号",temp); 
  477.   memcpy(temp,rtmp.Link.app.av_val,rtmp.Link.app.av_len); 
  478.   temp[rtmp.Link.app.av_len]=‘\0‘; 
  479.   rtmp.dlg->AppendB_R_L_Info("应用程序",temp); 
  480.   memcpy(temp,rtmp.Link.playpath.av_val,rtmp.Link.playpath.av_len); 
  481.   temp[rtmp.Link.playpath.av_len]=‘\0‘; 
  482.   rtmp.dlg->AppendB_R_L_Info("路径",temp); 
  483. //-----------------------------
  484. /* Try to keep the stream moving if it pauses on us */
  485. if (!bLiveStream && !(protocol & RTMP_FEATURE_HTTP)) 
  486.     rtmp.Link.lFlags |= RTMP_LF_BUFX; 
  487.   off_t size = 0; 
  488. // ok,我们必须获得timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
  489. if (bResume) 
  490.     { 
  491. //打开文件,输出的文件(Resume)
  492.       nStatus = 
  493.     OpenResumeFile(flvFile, &file, &size, &metaHeader, &nMetaHeaderSize, 
  494.                &duration); 
  495. if (nStatus == RD_FAILED) 
  496. goto clean; 
  497. if (!file) 
  498.     { 
  499. // file does not exist, so go back into normal mode
  500.       bResume = FALSE;  // we are back in fresh file mode (otherwise finalizing file won‘t be done)
  501.     } 
  502. else
  503.     { 
  504. //获取最后一个关键帧
  505.       nStatus = GetLastKeyframe(file, nSkipKeyFrames, 
  506.                     &dSeek, &initialFrame, 
  507.                     &initialFrameType, &nInitialFrameSize); 
  508. if (nStatus == RD_FAILED) 
  509.         { 
  510.           RTMP_Log(RTMP_LOGDEBUG, "Failed to get last keyframe."); 
  511. goto clean; 
  512.         } 
  513. if (dSeek == 0) 
  514.         { 
  515.           RTMP_Log(RTMP_LOGDEBUG, 
  516. "Last keyframe is first frame in stream, switching from resume to normal mode!"); 
  517.           bResume = FALSE; 
  518.         } 
  519.     } 
  520.     } 
  521. //如果输出文件不存在
  522. if (!file) 
  523.     { 
  524. if (bStdoutMode) 
  525.     { 
  526. //直接输出到stdout
  527.       file = stdout; 
  528.       SET_BINMODE(file); 
  529.     } 
  530. else
  531.     { 
  532. //打开一个文件
  533. //w+b 读写打开或建立一个二进制文件,允许读和写。
  534. //-----------------
  535.         rtmp.dlg->AppendCInfo("创建输出文件..."); 
  536. //-----------------------------
  537.       file = fopen(flvFile, "w+b"); 
  538. if (file == 0) 
  539.         { 
  540. //-----------------
  541.             rtmp.dlg->AppendCInfo("创建输出文件失败!"); 
  542. //-----------------------------
  543.           RTMP_LogPrintf("Failed to open file! %s\n", flvFile); 
  544. return RD_FAILED; 
  545.         } 
  546.       rtmp.dlg->AppendCInfo("成功创建输出文件"); 
  547.     } 
  548.     } 
  549. #ifdef _DEBUG
  550.   netstackdump = fopen("netstackdump", "wb"); 
  551.   netstackdump_read = fopen("netstackdump_read", "wb"); 
  552. #endif
  553. while (!RTMP_ctrlC) 
  554.     { 
  555.       RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime); 
  556. //设置Buffer时间
  557. //-----------------
  558.       rtmp.dlg->AppendCInfo("设置缓冲(Buffer)的时间"); 
  559. //-----------------------------
  560.       RTMP_SetBufferMS(&rtmp, bufferTime); 
  561. //第一次执行
  562. if (first) 
  563.     { 
  564.       first = 0; 
  565.       RTMP_LogPrintf("开始建立连接!\n"); 
  566. //-----------------
  567.       rtmp.dlg->AppendCInfo("开始建立连接(NetConnection)..."); 
  568. //-----------------------------
  569. //建立连接(Connect)
  570. if (!RTMP_Connect(&rtmp, NULL)) 
  571.         { 
  572. //-----------------
  573.             rtmp.dlg->AppendCInfo("建立连接(NetConnection)失败!"); 
  574. //-----------------------------
  575.           nStatus = RD_FAILED; 
  576. break; 
  577.         } 
  578. //-----------------
  579.       rtmp.dlg->AppendCInfo("成功建立连接(NetConnection)"); 
  580. //-----------------------------
  581. //RTMP_Log(RTMP_LOGINFO, "已链接...");
  582. // User defined seek offset
  583. if (dStartOffset > 0) 
  584.         { 
  585. // Don‘t need the start offset if resuming an existing file
  586. if (bResume) 
  587.         { 
  588.           RTMP_Log(RTMP_LOGWARNING, 
  589. "Can‘t seek a resumed stream, ignoring --start option"); 
  590.           dStartOffset = 0; 
  591.         } 
  592. else
  593.         { 
  594.           dSeek = dStartOffset; 
  595.         } 
  596.         } 
  597. // Calculate the length of the stream to still play
  598. if (dStopOffset > 0) 
  599.         { 
  600. // Quit if start seek is past required stop offset
  601. if (dStopOffset <= dSeek) 
  602.         { 
  603.           RTMP_LogPrintf("Already Completed\n"); 
  604.           nStatus = RD_SUCCESS; 
  605. break; 
  606.         } 
  607.         } 
  608. //创建流(Stream)(发送connect命令消息后处理传来的数据)
  609.       itoa(rtmp.m_inChunkSize,temp,10); 
  610.       rtmp.dlg->AppendB_R_Info("输入Chunk大小",temp); 
  611.       itoa(rtmp.m_outChunkSize,temp,10); 
  612.       rtmp.dlg->AppendB_R_Info("输出Chunk大小",temp); 
  613.       itoa(rtmp.m_stream_id,temp,10); 
  614.       rtmp.dlg->AppendB_R_Info("Stream ID",temp); 
  615.       itoa(rtmp.m_nBufferMS,temp,10); 
  616.       rtmp.dlg->AppendB_R_Info("Buffer时长(ms)",temp); 
  617.       itoa(rtmp.m_nServerBW,temp,10); 
  618.       rtmp.dlg->AppendB_R_Info("ServerBW",temp); 
  619.       itoa(rtmp.m_nClientBW,temp,10); 
  620.       rtmp.dlg->AppendB_R_Info("ClientBW",temp); 
  621.       itoa((int)rtmp.m_fEncoding,temp,10); 
  622.       rtmp.dlg->AppendB_R_Info("命令消息编码方法",temp); 
  623.       itoa((int)rtmp.m_fDuration,temp,10); 
  624.       rtmp.dlg->AppendB_R_Info("时长(s)",temp); 
  625.       rtmp.dlg->ShowBInfo(); 
  626.       free(temp); 
  627. //-----------------
  628.       rtmp.dlg->AppendCInfo("开始建立网络流(NetStream)"); 
  629. //-----------------------------
  630. if (!RTMP_ConnectStream(&rtmp, dSeek)) 
  631.         { 
  632. //-----------------
  633.         rtmp.dlg->AppendCInfo("建立网络流(NetStream)失败!"); 
  634. //-----------------
  635.           nStatus = RD_FAILED; 
  636. break; 
  637.         } 
  638. //-----------------
  639.       rtmp.dlg->AppendCInfo("成功建立网络流(NetStream)!"); 
  640. //-----------------
  641.     } 
  642. else
  643.     { 
  644.       nInitialFrameSize = 0; 
  645. if (retries) 
  646.             { 
  647.           RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); 
  648. if (!RTMP_IsTimedout(&rtmp)) 
  649.             nStatus = RD_FAILED; 
  650. else
  651.             nStatus = RD_INCOMPLETE; 
  652. break; 
  653.             } 
  654.       RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); 
  655. /* Did we already try pausing, and it still didn‘t work? */
  656. if (rtmp.m_pausing == 3) 
  657.             { 
  658. /* Only one try at reconnecting... */
  659.               retries = 1; 
  660.               dSeek = rtmp.m_pauseStamp; 
  661. if (dStopOffset > 0) 
  662.                 { 
  663. if (dStopOffset <= dSeek) 
  664.                     { 
  665.                       RTMP_LogPrintf("Already Completed\n"); 
  666.               nStatus = RD_SUCCESS; 
  667. break; 
  668.                     } 
  669.                 } 
  670. if (!RTMP_ReconnectStream(&rtmp, dSeek)) 
  671.                 { 
  672.               RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); 
  673. if (!RTMP_IsTimedout(&rtmp)) 
  674.             nStatus = RD_FAILED; 
  675. else
  676.             nStatus = RD_INCOMPLETE; 
  677. break; 
  678.                 } 
  679.             } 
  680. else if (!RTMP_ToggleStream(&rtmp)) 
  681.         { 
  682.           RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); 
  683. if (!RTMP_IsTimedout(&rtmp)) 
  684.         nStatus = RD_FAILED; 
  685. else
  686.         nStatus = RD_INCOMPLETE; 
  687. break; 
  688.         } 
  689.       bResume = TRUE; 
  690.     } 
  691. //-----------------
  692. //-----------------
  693.     rtmp.dlg->AppendCInfo("开始将媒体数据写入文件"); 
  694. //-----------------
  695. //下载,写入文件
  696.       nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, 
  697.              metaHeader, nMetaHeaderSize, initialFrame, 
  698.              initialFrameType, nInitialFrameSize, 
  699.              nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes, 
  700.              bOverrideBufferTime, bufferTime, &percent); 
  701.       free(initialFrame); 
  702.       initialFrame = NULL; 
  703. /* If we succeeded, we‘re done.
  704.        */
  705. if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) 
  706. break; 
  707.     } 
  708. //当下载完的时候
  709. if (nStatus == RD_SUCCESS) 
  710.     { 
  711. //-----------------
  712.         rtmp.dlg->AppendCInfo("写入文件完成"); 
  713. //-----------------
  714.       RTMP_LogPrintf("Download complete\n"); 
  715.     } 
  716. //没下载完的时候
  717. else if (nStatus == RD_INCOMPLETE) 
  718.     { 
  719. //-----------------
  720.         rtmp.dlg->AppendCInfo("写入文件可能不完整"); 
  721. //-----------------
  722.       RTMP_LogPrintf 
  723.     ("Download may be incomplete (downloaded about %.2f%%), try resuming\n", 
  724.      percent); 
  725.     } 
  726. //后续清理工作
  727. clean: 
  728. //-----------------
  729.   rtmp.dlg->AppendCInfo("关闭连接"); 
  730. //-----------------
  731.   RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); 
  732.   RTMP_Close(&rtmp); 
  733.   rtmp.dlg->AppendCInfo("关闭文件"); 
  734. if (file != 0) 
  735.     fclose(file); 
  736.   rtmp.dlg->AppendCInfo("关闭Socket"); 
  737.   CleanupSockets(); 
  738. #ifdef _DEBUG
  739. if (netstackdump != 0) 
  740.     fclose(netstackdump); 
  741. if (netstackdump_read != 0) 
  742.     fclose(netstackdump_read); 
  743. #endif
  744. return nStatus; 

其中InitSocket()代码很简单,初始化了Socket,如下:

[cpp] view plaincopy

  1. // 初始化 sockets
  2. int
  3. InitSockets() 
  4. #ifdef WIN32
  5. WORD version; 
  6.   WSADATA wsaData; 
  7.   version = MAKEWORD(1, 1); 
  8. return (WSAStartup(version, &wsaData) == 0); 
  9. #else
  10. return TRUE; 
  11. #endif

CleanupSockets()则更简单:

[cpp] view plaincopy

  1. inline void
  2. CleanupSockets() 
  3. #ifdef WIN32
  4.   WSACleanup(); 
  5. #endif

Download()函数则比较复杂:

[cpp] view plaincopy

  1. int
  2. Download(RTMP * rtmp,       // connected RTMP object
  3. FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent)   // percentage downloaded [out]
  4.   int32_t now, lastUpdate; 
  5. int bufferSize = 64 * 1024; 
  6. char *buffer = (char *) malloc(bufferSize); 
  7. int nRead = 0; 
  8. //long ftell(FILE *stream);
  9. //返回当前文件指针
  10.   RTMP_LogPrintf("开始下载!\n"); 
  11.   off_t size = ftello(file); 
  12.   unsigned long lastPercent = 0; 
  13. //时间戳
  14.   rtmp->m_read.timestamp = dSeek; 
  15.   *percent = 0.0; 
  16. if (rtmp->m_read.timestamp) 
  17.     { 
  18.       RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n", rtmp->m_read.timestamp); 
  19.     } 
  20. //是直播
  21. if (bLiveStream) 
  22.     { 
  23.       RTMP_LogPrintf("直播流\n"); 
  24.     } 
  25. else
  26.     { 
  27. // print initial status
  28. // Workaround to exit with 0 if the file is fully (> 99.9%) downloaded
  29. if (duration > 0) 
  30.     { 
  31. if ((double) rtmp->m_read.timestamp >= (double) duration * 999.0) 
  32.         { 
  33.           RTMP_LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n", 
  34.             (double) rtmp->m_read.timestamp / 1000.0, 
  35.             (double) duration / 1000.0); 
  36. return RD_SUCCESS; 
  37.         } 
  38. else
  39.         { 
  40.           *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0; 
  41.           *percent = ((double) (int) (*percent * 10.0)) / 10.0; 
  42.           RTMP_LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n", 
  43.             bResume ? "Resuming" : "Starting", 
  44.             (double) size / 1024.0, (double) rtmp->m_read.timestamp / 1000.0, 
  45.             *percent); 
  46.         } 
  47.     } 
  48. else
  49.     { 
  50.       RTMP_LogPrintf("%s download at: %.3f kB\n", 
  51.             bResume ? "Resuming" : "Starting", 
  52.             (double) size / 1024.0); 
  53.     } 
  54.     } 
  55. if (dStopOffset > 0) 
  56.     RTMP_LogPrintf("For duration: %.3f sec\n", (double) (dStopOffset - dSeek) / 1000.0); 
  57. //各种设置参数到rtmp连接
  58. if (bResume && nInitialFrameSize > 0) 
  59.   rtmp->m_read.flags |= RTMP_READ_RESUME; 
  60.   rtmp->m_read.initialFrameType = initialFrameType; 
  61.   rtmp->m_read.nResumeTS = dSeek; 
  62.   rtmp->m_read.metaHeader = metaHeader; 
  63.   rtmp->m_read.initialFrame = initialFrame; 
  64.   rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize; 
  65.   rtmp->m_read.nInitialFrameSize = nInitialFrameSize; 
  66.   now = RTMP_GetTime(); 
  67.   lastUpdate = now - 1000; 
  68. do
  69.     { 
  70. //从rtmp中把bufferSize(64k)个数据读入buffer
  71.       nRead = RTMP_Read(rtmp, buffer, bufferSize); 
  72. //RTMP_LogPrintf("nRead: %d\n", nRead);
  73. if (nRead > 0) 
  74.     { 
  75. //函数:size_t fwrite(const void* buffer,size_t size,size_t count,FILE* stream);
  76. //向文件读入写入一个数据块。返回值:返回实际写入的数据块数目
  77. //(1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
  78. //(2)size:要写入内容的单字节数; 
  79. //(3)count:要进行写入size字节的数据项的个数; 
  80. //(4)stream:目标文件指针。 
  81. //(5)返回实际写入的数据项个数count。
  82. //关键。把buffer里面的数据写成文件
  83. if (fwrite(buffer, sizeof(unsigned char), nRead, file) != 
  84.           (size_t) nRead) 
  85.         { 
  86.           RTMP_Log(RTMP_LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__); 
  87.           free(buffer); 
  88. return RD_FAILED; 
  89.         } 
  90. //记录已经写入的字节数
  91.       size += nRead; 
  92. //RTMP_LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0);
  93. if (duration <= 0) // if duration unknown try to get it from the stream (onMetaData)
  94.         duration = RTMP_GetDuration(rtmp); 
  95. if (duration > 0) 
  96.         { 
  97. // make sure we claim to have enough buffer time!
  98. if (!bOverrideBufferTime && bufferTime < (duration * 1000.0)) 
  99.         { 
  100.           bufferTime = (uint32_t) (duration * 1000.0) + 5000;   // 再加5s以确保buffertime足够长
  101.           RTMP_Log(RTMP_LOGDEBUG, 
  102. "Detected that buffer time is less than duration, resetting to: %dms", 
  103.               bufferTime); 
  104. //重设Buffer长度
  105.           RTMP_SetBufferMS(rtmp, bufferTime); 
  106. //给服务器发送UserControl消息通知Buffer改变
  107.           RTMP_UpdateBufferMS(rtmp); 
  108.         } 
  109. //计算百分比
  110.           *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0; 
  111.           *percent = ((double) (int) (*percent * 10.0)) / 10.0; 
  112. if (bHashes) 
  113.         { 
  114. if (lastPercent + 1 <= *percent) 
  115.             { 
  116.               RTMP_LogStatus("#"); 
  117.               lastPercent = (unsigned long) *percent; 
  118.             } 
  119.         } 
  120. else
  121.         { 
  122. //设置显示数据的更新间隔200ms
  123.           now = RTMP_GetTime(); 
  124. if (abs(now - lastUpdate) > 200) 
  125.             { 
  126.               RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", 
  127.                 (double) size / 1024.0, 
  128.                 (double) (rtmp->m_read.timestamp) / 1000.0, *percent); 
  129.               lastUpdate = now; 
  130.             } 
  131.         } 
  132.         } 
  133. else
  134.         { 
  135. //现在距离开机的毫秒数
  136.           now = RTMP_GetTime(); 
  137. //每间隔200ms刷新一次数据
  138. if (abs(now - lastUpdate) > 200) 
  139.         { 
  140. if (bHashes) 
  141.             RTMP_LogStatus("#"); 
  142. else
  143. //size为已写入文件的字节数
  144.             RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0, 
  145.                   (double) (rtmp->m_read.timestamp) / 1000.0); 
  146.           lastUpdate = now; 
  147.         } 
  148.         } 
  149.     } 
  150. #ifdef _DEBUG
  151. else
  152.     { 
  153.       RTMP_Log(RTMP_LOGDEBUG, "zero read!"); 
  154.     } 
  155. #endif
  156.     } 
  157. while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp)); 
  158.   free(buffer); 
  159. if (nRead < 0) 
  160. //nRead是读取情况
  161.     nRead = rtmp->m_read.status; 
  162. /* Final status update */
  163. if (!bHashes) 
  164.     { 
  165. if (duration > 0) 
  166.     { 
  167.       *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0; 
  168.       *percent = ((double) (int) (*percent * 10.0)) / 10.0; 
  169. //输出
  170.       RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", 
  171.         (double) size / 1024.0, 
  172.         (double) (rtmp->m_read.timestamp) / 1000.0, *percent); 
  173.     } 
  174. else
  175.     { 
  176.       RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0, 
  177.         (double) (rtmp->m_read.timestamp) / 1000.0); 
  178.     } 
  179.     } 
  180.   RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead); 
  181. //读取错误
  182. if (bResume && nRead == -2) 
  183.     { 
  184.       RTMP_LogPrintf("Couldn‘t resume FLV file, try --skip %d\n\n", 
  185.         nSkipKeyFrames + 1); 
  186. return RD_FAILED; 
  187.     } 
  188. //读取正确
  189. if (nRead == -3) 
  190. return RD_SUCCESS; 
  191. //没读完...
  192. if ((duration > 0 && *percent < 99.9) || RTMP_ctrlC || nRead < 0 
  193.       || RTMP_IsTimedout(rtmp)) 
  194.     { 
  195. return RD_INCOMPLETE; 
  196.     } 
  197. return RD_SUCCESS; 

以上内容是我能理解到的rtmpdump.c里面的内容。

2:解析RTMP地址——RTMP_ParseURL()

之前分析了一下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):

[cpp] view plaincopy

  1. /*
  2. * 本文件主要包含了对输入URL的解析
  3. */
  4. #include "stdafx.h"
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <assert.h>
  8. #include <ctype.h>
  9. #include "rtmp_sys.h"
  10. #include "log.h"
  11. /*解析URL,得到协议名称(protocol),主机名称(host),应用程序名称(app)
  12. *
  13. */
  14. int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, 
  15.     AVal *playpath, AVal *app) 
  16. char *p, *end, *col, *ques, *slash; 
  17.     RTMP_Log(RTMP_LOGDEBUG, "Parsing..."); 
  18.     *protocol = RTMP_PROTOCOL_RTMP; 
  19.     *port = 0; 
  20.     playpath->av_len = 0; 
  21.     playpath->av_val = NULL; 
  22.     app->av_len = 0; 
  23.     app->av_val = NULL; 
  24. /* 字符串解析 */
  25. /* 查找“://”  */
  26. //函数原型:char *strstr(char *str1, char *str2);
  27. //功能:找出str2字符串在str1字符串中第一次出现的位置(不包括str2的串结束符)。
  28. //返回值:返回该位置的指针,如找不到,返回空指针。
  29.     p = strstr((char *)url, "://"); 
  30. if(!p) { 
  31.         RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!"); 
  32. return FALSE; 
  33.     } 
  34.     { 
  35. //指针相减,返回“://”之前字符串长度len
  36. int len = (int)(p-url); 
  37. //获取使用的协议
  38. //通过比较字符串的方法
  39. if(len == 4 && strncasecmp(url, "rtmp", 4)==0) 
  40.         *protocol = RTMP_PROTOCOL_RTMP; 
  41. else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0) 
  42.         *protocol = RTMP_PROTOCOL_RTMPT; 
  43. else if(len == 5 && strncasecmp(url, "rtmps", 5)==0) 
  44.             *protocol = RTMP_PROTOCOL_RTMPS; 
  45. else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0) 
  46.             *protocol = RTMP_PROTOCOL_RTMPE; 
  47. else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0) 
  48.             *protocol = RTMP_PROTOCOL_RTMFP; 
  49. else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0) 
  50.             *protocol = RTMP_PROTOCOL_RTMPTE; 
  51. else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0) 
  52.             *protocol = RTMP_PROTOCOL_RTMPTS; 
  53. else { 
  54.         RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n"); 
  55. goto parsehost; 
  56.     } 
  57.     } 
  58.     RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol); 
  59. parsehost: 
  60. //获取主机名称
  61. //跳过“://”
  62.     p+=3; 
  63. /* 检查一下主机名 */
  64. if(*p==0) { 
  65.         RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!"); 
  66. return FALSE; 
  67.     } 
  68. //原型:char *strchr(const char *s,char c);
  69. //功能:查找字符串s中首次出现字符c的位置
  70. //说明:返回首次出现c的位置的指针,如果s中不存在c则返回NULL。
  71.     end   = p + strlen(p);//指向结尾的指针
  72.     col   = strchr(p, ‘:‘);//指向冒号(第一个)的指针
  73.     ques  = strchr(p, ‘?‘);//指向问号(第一个)的指针
  74.     slash = strchr(p, ‘/‘);//指向斜杠(第一个)的指针
  75.     { 
  76. int hostlen; 
  77. if(slash) 
  78.         hostlen = slash - p; 
  79. else
  80.         hostlen = end - p; 
  81. if(col && col -p < hostlen) 
  82.         hostlen = col - p; 
  83. if(hostlen < 256) { 
  84.         host->av_val = p; 
  85.         host->av_len = hostlen; 
  86.         RTMP_Log(RTMP_LOGDEBUG, "Parsed host    : %.*s", hostlen, host->av_val); 
  87.     } else { 
  88.         RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!"); 
  89.     } 
  90.     p+=hostlen; 
  91.     } 
  92. /* 获取端口号 */
  93. if(*p == ‘:‘) { 
  94.         unsigned int p2; 
  95.         p++; 
  96.         p2 = atoi(p); 
  97. if(p2 > 65535) { 
  98.             RTMP_Log(RTMP_LOGWARNING, "Invalid port number!"); 
  99.         } else { 
  100.             *port = p2; 
  101.         } 
  102.     } 
  103. if(!slash) { 
  104.         RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!"); 
  105. return TRUE; 
  106.     } 
  107.     p = slash+1; 
  108.     { 
  109. /* 获取应用程序(application)
  110.      *
  111.      * rtmp://host[:port]/app[/appinstance][/...]
  112.      * application = app[/appinstance]
  113.      */
  114. char *slash2, *slash3 = NULL;//指向第二个斜杠,第三个斜杠的指针
  115. int applen, appnamelen; 
  116.     slash2 = strchr(p, ‘/‘);//指向第二个斜杠
  117. if(slash2) 
  118.         slash3 = strchr(slash2+1, ‘/‘);//指向第三个斜杠,注意slash2之所以+1是因为让其后移一位
  119.     applen = end-p; /* ondemand, pass all parameters as app */
  120.     appnamelen = applen; /* ondemand length */
  121. if(ques && strstr(p, "slist=")) { /* whatever it is, the ‘?‘ and slist= means we need to use everything as app and parse plapath from slist= */
  122.         appnamelen = ques-p; 
  123.     } 
  124. else if(strncmp(p, "ondemand/", 9)==0) { 
  125. /* app = ondemand/foobar, only pass app=ondemand */
  126.                 applen = 8; 
  127.                 appnamelen = 8; 
  128.         } 
  129. else { /* app!=ondemand, so app is app[/appinstance] */
  130. if(slash3) 
  131.             appnamelen = slash3-p; 
  132. else if(slash2) 
  133.             appnamelen = slash2-p; 
  134.         applen = appnamelen; 
  135.     } 
  136.     app->av_val = p; 
  137.     app->av_len = applen; 
  138.     RTMP_Log(RTMP_LOGDEBUG, "Parsed app     : %.*s", applen, p); 
  139.     p += appnamelen; 
  140.     } 
  141. if (*p == ‘/‘) 
  142.         p++; 
  143. if (end-p) { 
  144.         AVal av = {p, end-p}; 
  145.         RTMP_ParsePlaypath(&av, playpath); 
  146.     } 
  147. return TRUE; 
  148. /*
  149. * 从URL中获取播放路径(playpath)。播放路径是URL中“rtmp://host:port/app/”后面的部分
  150. *
  151. * 获取FMS能够识别的播放路径
  152. * mp4 流: 前面添加 "mp4:", 删除扩展名
  153. * mp3 流: 前面添加 "mp3:", 删除扩展名
  154. * flv 流: 删除扩展名
  155. */
  156. void RTMP_ParsePlaypath(AVal *in, AVal *out) { 
  157. int addMP4 = 0; 
  158. int addMP3 = 0; 
  159. int subExt = 0; 
  160. const char *playpath = in->av_val; 
  161. const char *temp, *q, *ext = NULL; 
  162. const char *ppstart = playpath; 
  163. char *streamname, *destptr, *p; 
  164. int pplen = in->av_len; 
  165.     out->av_val = NULL; 
  166.     out->av_len = 0; 
  167. if ((*ppstart == ‘?‘) && 
  168.         (temp=strstr(ppstart, "slist=")) != 0) { 
  169.         ppstart = temp+6; 
  170.         pplen = strlen(ppstart); 
  171.         temp = strchr(ppstart, ‘&‘); 
  172. if (temp) { 
  173.             pplen = temp-ppstart; 
  174.         } 
  175.     } 
  176.     q = strchr(ppstart, ‘?‘); 
  177. if (pplen >= 4) { 
  178. if (q) 
  179.             ext = q-4; 
  180. else
  181.             ext = &ppstart[pplen-4]; 
  182. if ((strncmp(ext, ".f4v", 4) == 0) || 
  183.             (strncmp(ext, ".mp4", 4) == 0)) { 
  184.             addMP4 = 1; 
  185.             subExt = 1; 
  186. /* Only remove .flv from rtmp URL, not slist params */
  187.         } else if ((ppstart == playpath) && 
  188.             (strncmp(ext, ".flv", 4) == 0)) { 
  189.             subExt = 1; 
  190.         } else if (strncmp(ext, ".mp3", 4) == 0) { 
  191.             addMP3 = 1; 
  192.             subExt = 1; 
  193.         } 
  194.     } 
  195.     streamname = (char *)malloc((pplen+4+1)*sizeof(char)); 
  196. if (!streamname) 
  197. return; 
  198.     destptr = streamname; 
  199. if (addMP4) { 
  200. if (strncmp(ppstart, "mp4:", 4)) { 
  201.             strcpy(destptr, "mp4:"); 
  202.             destptr += 4; 
  203.         } else { 
  204.             subExt = 0; 
  205.         } 
  206.     } else if (addMP3) { 
  207. if (strncmp(ppstart, "mp3:", 4)) { 
  208.             strcpy(destptr, "mp3:"); 
  209.             destptr += 4; 
  210.         } else { 
  211.             subExt = 0; 
  212.         } 
  213.     } 
  214. for (p=(char *)ppstart; pplen >0;) { 
  215. /* skip extension */
  216. if (subExt && p == ext) { 
  217.             p += 4; 
  218.             pplen -= 4; 
  219. continue; 
  220.         } 
  221. if (*p == ‘%‘) { 
  222.             unsigned int c; 
  223.             sscanf(p+1, "%02x", &c); 
  224.             *destptr++ = c; 
  225.             pplen -= 3; 
  226.             p += 3; 
  227.         } else { 
  228.             *destptr++ = *p++; 
  229.             pplen--; 
  230.         } 
  231.     } 
  232.     *destptr = ‘\0‘; 
  233.     out->av_val = streamname; 
  234.     out->av_len = destptr - streamname; 

3: AMF编码

之前分析了RTMPDump(libRTMP)解析RTMP的URL的源代码,在这里简单分析一下其AMF编码方面的源码。

AMF编码广泛用于Adobe公司的Flash以及Flex系统中。由于RTMP协议也是Adobe公司的,所以它也使用AMF进行通信。具体 AMF是怎么使用的在这里就不做详细讨论了。RTMPDump如果想实现RTMP协议的流媒体的下载保存,就必须可以编码和解码AMF格式的数据。

amf.c是RTMPDump解析RTMP协议的函数存放的地方,在这里贴上其源代码。先不做详细解释了,以后有机会再补充。

[cpp] view plaincopy

  1. #include "stdafx.h"
  2. /*  本文件主要包含了对AMF对象的操作
  3. *-------------------------------------
  4. *AMF数据类型:
  5. *Type      Byte code
  6. *Number    0x00
  7. *Boolean   0x01
  8. *String    0x02
  9. *Object    0x03
  10. *MovieClip 0x04
  11. *Null      0x05
  12. *Undefined 0x06
  13. *Reference 0x07
  14. *MixedArray    0x08
  15. *EndOfObject   0x09
  16. *Array         0x0a
  17. *Date          0x0b
  18. *LongString    0x0c
  19. *Unsupported   0x0d
  20. *Recordset     0x0e
  21. *XML           0x0f
  22. *TypedObject (Class instance)  0x10
  23. *AMF3 data 0×11
  24. *--------------------------------------
  25. *应用举例:
  26. *0.Number这里指的是double类型,数据用8字节表示,比如十六进制00 40 10 00 00 00 00 00 00就表示的是一个double数4.0
  27. *1.Boolean对应的是.net中的bool类型,数据使用1字节表示,和C语言差不多,使用00表示false,使用01表示true。比如十六进制01 01就表示true。
  28. *2.String相当于.net中的string类型,String所占用的空间有1个类型标识字节和2个表示字符串UTF8长度的字节加上字符串UTF8格式的内容组成。
  29. *  比如十六进制03 00 08 73 68 61 6E 67 67 75 61表示的就是字符串,该字符串长8字节,字符串内容为73 68 61 6E 67 67 75 61,对应的就是“shanggua”。
  30. *3.Object在对应的就是Hashtable,内容由UTF8字符串作为Key,其他AMF类型作为Value,该对象由3个字节:00 00 09来表示结束。
  31. *5.Null就是空对象,该对象只占用一个字节,那就是Null对象标识0x05。
  32. *6.Undefined 也是只占用一个字节0x06。
  33. *8.MixedArray相当于Hashtable,与3不同的是该对象定义了Hashtable的大小。
  34. */
  35. #include <string.h>
  36. #include <assert.h>
  37. #include <stdlib.h>
  38. #include "rtmp_sys.h"
  39. #include "amf.h"
  40. #include "log.h"
  41. #include "bytes.h"
  42. static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID }; 
  43. static const AVal AV_empty = { 0, 0 }; 
  44. //大端Big-Endian
  45. //低地址存放最高有效位(MSB),既高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
  46. //符合人脑逻辑,与计算机逻辑不同
  47. //网络字节序 Network Order:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使
  48. //用的字节序通常称之为网络字节序。
  49. //主机序 Host Orader:它遵循Little-Endian规则。所以当两台主机之间要通过TCP/IP协议进行通
  50. //信的时候就需要调用相应的函数进行主机序(Little-Endian)和网络序(Big-Endian)的转换。
  51. /*AMF数据采用 Big-Endian(大端模式),主机采用Little-Endian(小端模式) */
  52. unsigned short
  53. AMF_DecodeInt16(const char *data) 
  54.   unsigned char *c = (unsigned char *) data; 
  55.   unsigned short val; 
  56.   val = (c[0] << 8) | c[1];//转换
  57. return val; 
  58. unsigned int
  59. AMF_DecodeInt24(const char *data) 
  60.   unsigned char *c = (unsigned char *) data; 
  61.   unsigned int val; 
  62.   val = (c[0] << 16) | (c[1] << 8) | c[2]; 
  63. return val; 
  64. unsigned int
  65. AMF_DecodeInt32(const char *data) 
  66.   unsigned char *c = (unsigned char *)data; 
  67.   unsigned int val; 
  68.   val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; 
  69. return val; 
  70. void
  71. AMF_DecodeString(const char *data, AVal *bv) 
  72.   bv->av_len = AMF_DecodeInt16(data); 
  73.   bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL; 
  74. void
  75. AMF_DecodeLongString(const char *data, AVal *bv) 
  76.   bv->av_len = AMF_DecodeInt32(data); 
  77.   bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL; 
  78. double
  79. AMF_DecodeNumber(const char *data) 
  80. double dVal; 
  81. #if __FLOAT_WORD_ORDER == __BYTE_ORDER
  82. #if __BYTE_ORDER == __BIG_ENDIAN
  83.   memcpy(&dVal, data, 8); 
  84. #elif __BYTE_ORDER == __LITTLE_ENDIAN
  85.   unsigned char *ci, *co; 
  86.   ci = (unsigned char *)data; 
  87.   co = (unsigned char *)&dVal; 
  88.   co[0] = ci[7]; 
  89.   co[1] = ci[6]; 
  90.   co[2] = ci[5]; 
  91.   co[3] = ci[4]; 
  92.   co[4] = ci[3]; 
  93.   co[5] = ci[2]; 
  94.   co[6] = ci[1]; 
  95.   co[7] = ci[0]; 
  96. #endif
  97. #else
  98. #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
  99.   unsigned char *ci, *co; 
  100.   ci = (unsigned char *)data; 
  101.   co = (unsigned char *)&dVal; 
  102.   co[0] = ci[3]; 
  103.   co[1] = ci[2]; 
  104.   co[2] = ci[1]; 
  105.   co[3] = ci[0]; 
  106.   co[4] = ci[7]; 
  107.   co[5] = ci[6]; 
  108.   co[6] = ci[5]; 
  109.   co[7] = ci[4]; 
  110. #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
  111.   unsigned char *ci, *co; 
  112.   ci = (unsigned char *)data; 
  113.   co = (unsigned char *)&dVal; 
  114.   co[0] = ci[4]; 
  115.   co[1] = ci[5]; 
  116.   co[2] = ci[6]; 
  117.   co[3] = ci[7]; 
  118.   co[4] = ci[0]; 
  119.   co[5] = ci[1]; 
  120.   co[6] = ci[2]; 
  121.   co[7] = ci[3]; 
  122. #endif
  123. #endif
  124. return dVal; 
  125. int
  126. AMF_DecodeBoolean(const char *data) 
  127. return *data != 0; 
  128. char * 
  129. AMF_EncodeInt16(char *output, char *outend, short nVal) 
  130. if (output+2 > outend) 
  131. return NULL; 
  132.   output[1] = nVal & 0xff; 
  133.   output[0] = nVal >> 8; 
  134. return output+2; 
  135. //3字节的int数据进行AMF编码,AMF采用大端模式
  136. char * 
  137. AMF_EncodeInt24(char *output, char *outend, int nVal) 
  138. if (output+3 > outend) 
  139. return NULL; 
  140. //倒过来
  141.   output[2] = nVal & 0xff; 
  142.   output[1] = nVal >> 8; 
  143.   output[0] = nVal >> 16; 
  144. //返回指针指向编码后数据的尾部
  145. return output+3; 
  146. char * 
  147. AMF_EncodeInt32(char *output, char *outend, int nVal) 
  148. if (output+4 > outend) 
  149. return NULL; 
  150.   output[3] = nVal & 0xff; 
  151.   output[2] = nVal >> 8; 
  152.   output[1] = nVal >> 16; 
  153.   output[0] = nVal >> 24; 
  154. return output+4; 
  155. char * 
  156. AMF_EncodeString(char *output, char *outend, const AVal *bv) 
  157. if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) || 
  158.     output + 1 + 4 + bv->av_len > outend) 
  159. return NULL; 
  160. if (bv->av_len < 65536) 
  161.     { 
  162.       *output++ = AMF_STRING; 
  163.       output = AMF_EncodeInt16(output, outend, bv->av_len); 
  164.     } 
  165. else
  166.     { 
  167.       *output++ = AMF_LONG_STRING; 
  168.       output = AMF_EncodeInt32(output, outend, bv->av_len); 
  169.     } 
  170.   memcpy(output, bv->av_val, bv->av_len); 
  171.   output += bv->av_len; 
  172. return output; 
  173. char * 
  174. AMF_EncodeNumber(char *output, char *outend, double dVal) 
  175. if (output+1+8 > outend) 
  176. return NULL; 
  177.   *output++ = AMF_NUMBER;   /* type: Number */
  178. #if __FLOAT_WORD_ORDER == __BYTE_ORDER
  179. #if __BYTE_ORDER == __BIG_ENDIAN
  180.   memcpy(output, &dVal, 8); 
  181. #elif __BYTE_ORDER == __LITTLE_ENDIAN
  182.   { 
  183.     unsigned char *ci, *co; 
  184.     ci = (unsigned char *)&dVal; 
  185.     co = (unsigned char *)output; 
  186.     co[0] = ci[7]; 
  187.     co[1] = ci[6]; 
  188.     co[2] = ci[5]; 
  189.     co[3] = ci[4]; 
  190.     co[4] = ci[3]; 
  191.     co[5] = ci[2]; 
  192.     co[6] = ci[1]; 
  193.     co[7] = ci[0]; 
  194.   } 
  195. #endif
  196. #else
  197. #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
  198.   { 
  199.     unsigned char *ci, *co; 
  200.     ci = (unsigned char *)&dVal; 
  201.     co = (unsigned char *)output; 
  202.     co[0] = ci[3]; 
  203.     co[1] = ci[2]; 
  204.     co[2] = ci[1]; 
  205.     co[3] = ci[0]; 
  206.     co[4] = ci[7]; 
  207.     co[5] = ci[6]; 
  208.     co[6] = ci[5]; 
  209.     co[7] = ci[4]; 
  210.   } 
  211. #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
  212.   { 
  213.     unsigned char *ci, *co; 
  214.     ci = (unsigned char *)&dVal; 
  215.     co = (unsigned char *)output; 
  216.     co[0] = ci[4]; 
  217.     co[1] = ci[5]; 
  218.     co[2] = ci[6]; 
  219.     co[3] = ci[7]; 
  220.     co[4] = ci[0]; 
  221.     co[5] = ci[1]; 
  222.     co[6] = ci[2]; 
  223.     co[7] = ci[3]; 
  224.   } 
  225. #endif
  226. #endif
  227. return output+8; 
  228. char * 
  229. AMF_EncodeBoolean(char *output, char *outend, int bVal) 
  230. if (output+2 > outend) 
  231. return NULL; 
  232.   *output++ = AMF_BOOLEAN; 
  233.   *output++ = bVal ? 0x01 : 0x00; 
  234. return output; 
  235. char * 
  236. AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue) 
  237. if (output+2+strName->av_len > outend) 
  238. return NULL; 
  239.   output = AMF_EncodeInt16(output, outend, strName->av_len); 
  240.   memcpy(output, strName->av_val, strName->av_len); 
  241.   output += strName->av_len; 
  242. return AMF_EncodeString(output, outend, strValue); 
  243. char * 
  244. AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal) 
  245. if (output+2+strName->av_len > outend) 
  246. return NULL; 
  247.   output = AMF_EncodeInt16(output, outend, strName->av_len); 
  248.   memcpy(output, strName->av_val, strName->av_len); 
  249.   output += strName->av_len; 
  250. return AMF_EncodeNumber(output, outend, dVal); 
  251. char * 
  252. AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal) 
  253. if (output+2+strName->av_len > outend) 
  254. return NULL; 
  255.   output = AMF_EncodeInt16(output, outend, strName->av_len); 
  256.   memcpy(output, strName->av_val, strName->av_len); 
  257.   output += strName->av_len; 
  258. return AMF_EncodeBoolean(output, outend, bVal); 
  259. void
  260. AMFProp_GetName(AMFObjectProperty *prop, AVal *name) 
  261.   *name = prop->p_name; 
  262. void
  263. AMFProp_SetName(AMFObjectProperty *prop, AVal *name) 
  264.   prop->p_name = *name; 
  265. AMFDataType 
  266. AMFProp_GetType(AMFObjectProperty *prop) 
  267. return prop->p_type; 
  268. double
  269. AMFProp_GetNumber(AMFObjectProperty *prop) 
  270. return prop->p_vu.p_number; 
  271. int
  272. AMFProp_GetBoolean(AMFObjectProperty *prop) 
  273. return prop->p_vu.p_number != 0; 
  274. void
  275. AMFProp_GetString(AMFObjectProperty *prop, AVal *str) 
  276.   *str = prop->p_vu.p_aval; 
  277. void
  278. AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj) 
  279.   *obj = prop->p_vu.p_object; 
  280. int
  281. AMFProp_IsValid(AMFObjectProperty *prop) 
  282. return prop->p_type != AMF_INVALID; 
  283. char * 
  284. AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd) 
  285. if (prop->p_type == AMF_INVALID) 
  286. return NULL; 
  287. if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd) 
  288. return NULL; 
  289. if (prop->p_type != AMF_NULL && prop->p_name.av_len) 
  290.     { 
  291.       *pBuffer++ = prop->p_name.av_len >> 8; 
  292.       *pBuffer++ = prop->p_name.av_len & 0xff; 
  293.       memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len); 
  294.       pBuffer += prop->p_name.av_len; 
  295.     } 
  296. switch (prop->p_type) 
  297.     { 
  298. case AMF_NUMBER: 
  299.       pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number); 
  300. break; 
  301. case AMF_BOOLEAN: 
  302.       pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0); 
  303. break; 
  304. case AMF_STRING: 
  305.       pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval); 
  306. break; 
  307. case AMF_NULL: 
  308. if (pBuffer+1 >= pBufEnd) 
  309. return NULL; 
  310.       *pBuffer++ = AMF_NULL; 
  311. break; 
  312. case AMF_OBJECT: 
  313.       pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd); 
  314. break; 
  315. default: 
  316.       RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type); 
  317.       pBuffer = NULL; 
  318.     }; 
  319. return pBuffer; 
  320. #define AMF3_INTEGER_MAX    268435455
  321. #define AMF3_INTEGER_MIN    -268435456
  322. int
  323. AMF3ReadInteger(const char *data, int32_t *valp) 
  324. int i = 0; 
  325.   int32_t val = 0; 
  326. while (i <= 2) 
  327.     {               /* handle first 3 bytes */
  328. if (data[i] & 0x80) 
  329.     {           /* byte used */
  330.       val <<= 7;      /* shift up */
  331.       val |= (data[i] & 0x7f);  /* add bits */
  332.       i++; 
  333.     } 
  334. else
  335.     { 
  336. break; 
  337.     } 
  338.     } 
  339. if (i > 2) 
  340.     {               /* use 4th byte, all 8bits */
  341.       val <<= 8; 
  342.       val |= data[3]; 
  343. /* range check */
  344. if (val > AMF3_INTEGER_MAX) 
  345.     val -= (1 << 29); 
  346.     } 
  347. else
  348.     {               /* use 7bits of last unparsed byte (0xxxxxxx) */
  349.       val <<= 7; 
  350.       val |= data[i]; 
  351.     } 
  352.   *valp = val; 
  353. return i > 2 ? 4 : i + 1; 
  354. int
  355. AMF3ReadString(const char *data, AVal *str) 
  356.   int32_t ref = 0; 
  357. int len; 
  358.   assert(str != 0); 
  359.   len = AMF3ReadInteger(data, &ref); 
  360.   data += len; 
  361. if ((ref & 0x1) == 0) 
  362.     {               /* reference: 0xxx */
  363.       uint32_t refIndex = (ref >> 1); 
  364.       RTMP_Log(RTMP_LOGDEBUG, 
  365. "%s, string reference, index: %d, not supported, ignoring!", 
  366.       __FUNCTION__, refIndex); 
  367. return len; 
  368.     } 
  369. else
  370.     { 
  371.       uint32_t nSize = (ref >> 1); 
  372.       str->av_val = (char *)data; 
  373.       str->av_len = nSize; 
  374. return len + nSize; 
  375.     } 
  376. return len; 
  377. int
  378. AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize, 
  379. int bDecodeName) 
  380. int nOriginalSize = nSize; 
  381.   AMF3DataType type; 
  382.   prop->p_name.av_len = 0; 
  383.   prop->p_name.av_val = NULL; 
  384. if (nSize == 0 || !pBuffer) 
  385.     { 
  386.       RTMP_Log(RTMP_LOGDEBUG, "empty buffer/no buffer pointer!"); 
  387. return -1; 
  388.     } 
  389. /* decode name */
  390. if (bDecodeName) 
  391.     { 
  392.       AVal name; 
  393. int nRes = AMF3ReadString(pBuffer, &name); 
  394. if (name.av_len <= 0) 
  395. return nRes; 
  396.       prop->p_name = name; 
  397.       pBuffer += nRes; 
  398.       nSize -= nRes; 
  399.     } 
  400. /* decode */
  401.   type = (AMF3DataType) *pBuffer++; 
  402.   nSize--; 
  403. switch (type) 
  404.     { 
  405. case AMF3_UNDEFINED: 
  406. case AMF3_NULL: 
  407.       prop->p_type = AMF_NULL; 
  408. break; 
  409. case AMF3_FALSE: 
  410.       prop->p_type = AMF_BOOLEAN; 
  411.       prop->p_vu.p_number = 0.0; 
  412. break; 
  413. case AMF3_TRUE: 
  414.       prop->p_type = AMF_BOOLEAN; 
  415.       prop->p_vu.p_number = 1.0; 
  416. break; 
  417. case AMF3_INTEGER: 
  418.       { 
  419.     int32_t res = 0; 
  420. int len = AMF3ReadInteger(pBuffer, &res); 
  421.     prop->p_vu.p_number = (double)res; 
  422.     prop->p_type = AMF_NUMBER; 
  423.     nSize -= len; 
  424. break; 
  425.       } 
  426. case AMF3_DOUBLE: 
  427. if (nSize < 8) 
  428. return -1; 
  429.       prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 
  430.       prop->p_type = AMF_NUMBER; 
  431.       nSize -= 8; 
  432. break; 
  433. case AMF3_STRING: 
  434. case AMF3_XML_DOC: 
  435. case AMF3_XML: 
  436.       { 
  437. int len = AMF3ReadString(pBuffer, &prop->p_vu.p_aval); 
  438.     prop->p_type = AMF_STRING; 
  439.     nSize -= len; 
  440. break; 
  441.       } 
  442. case AMF3_DATE: 
  443.       { 
  444.     int32_t res = 0; 
  445. int len = AMF3ReadInteger(pBuffer, &res); 
  446.     nSize -= len; 
  447.     pBuffer += len; 
  448. if ((res & 0x1) == 0) 
  449.       {         /* reference */
  450.         uint32_t nIndex = (res >> 1); 
  451.         RTMP_Log(RTMP_LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex); 
  452.       } 
  453. else
  454.       { 
  455. if (nSize < 8) 
  456. return -1; 
  457.         prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 
  458.         nSize -= 8; 
  459.         prop->p_type = AMF_NUMBER; 
  460.       } 
  461. break; 
  462.       } 
  463. case AMF3_OBJECT: 
  464.       { 
  465. int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE); 
  466. if (nRes == -1) 
  467. return -1; 
  468.     nSize -= nRes; 
  469.     prop->p_type = AMF_OBJECT; 
  470. break; 
  471.       } 
  472. case AMF3_ARRAY: 
  473. case AMF3_BYTE_ARRAY: 
  474. default: 
  475.       RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @0x%08X", 
  476.       __FUNCTION__, (unsigned char)(*pBuffer), pBuffer); 
  477. return -1; 
  478.     } 
  479. return nOriginalSize - nSize; 
  480. //对AMF数据类型解析
  481. int
  482. AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize, 
  483. int bDecodeName) 
  484. int nOriginalSize = nSize; 
  485. int nRes; 
  486.   prop->p_name.av_len = 0; 
  487.   prop->p_name.av_val = NULL; 
  488. if (nSize == 0 || !pBuffer) 
  489.     { 
  490.       RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); 
  491. return -1; 
  492.     } 
  493. if (bDecodeName && nSize < 4) 
  494.     {               /* at least name (length + at least 1 byte) and 1 byte of data */
  495.       RTMP_Log(RTMP_LOGDEBUG, 
  496. "%s: Not enough data for decoding with name, less than 4 bytes!", 
  497.       __FUNCTION__); 
  498. return -1; 
  499.     } 
  500. if (bDecodeName) 
  501.     { 
  502.       unsigned short nNameSize = AMF_DecodeInt16(pBuffer); 
  503. if (nNameSize > nSize - 2) 
  504.     { 
  505.       RTMP_Log(RTMP_LOGDEBUG, 
  506. "%s: Name size out of range: namesize (%d) > len (%d) - 2", 
  507.           __FUNCTION__, nNameSize, nSize); 
  508. return -1; 
  509.     } 
  510.       AMF_DecodeString(pBuffer, &prop->p_name); 
  511.       nSize -= 2 + nNameSize; 
  512.       pBuffer += 2 + nNameSize; 
  513.     } 
  514. if (nSize == 0) 
  515.     { 
  516. return -1; 
  517.     } 
  518.   nSize--; 
  519.   prop->p_type = (AMFDataType) *pBuffer++; 
  520. switch (prop->p_type) 
  521.     { 
  522. //Number数据类型
  523. case AMF_NUMBER: 
  524. if (nSize < 8) 
  525. return -1; 
  526.       prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 
  527.       nSize -= 8; 
  528. break; 
  529. //Boolean数据类型
  530. case AMF_BOOLEAN: 
  531. if (nSize < 1) 
  532. return -1; 
  533.       prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer); 
  534.       nSize--; 
  535. break; 
  536. //String数据类型
  537. case AMF_STRING: 
  538.       { 
  539.     unsigned short nStringSize = AMF_DecodeInt16(pBuffer); 
  540. if (nSize < (long)nStringSize + 2) 
  541. return -1; 
  542.     AMF_DecodeString(pBuffer, &prop->p_vu.p_aval); 
  543.     nSize -= (2 + nStringSize); 
  544. break; 
  545.       } 
  546. //Object数据类型
  547. case AMF_OBJECT: 
  548.       { 
  549. int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE); 
  550. if (nRes == -1) 
  551. return -1; 
  552.     nSize -= nRes; 
  553. break; 
  554.       } 
  555. case AMF_MOVIECLIP: 
  556.       { 
  557.     RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!"); 
  558. return -1; 
  559. break; 
  560.       } 
  561. case AMF_NULL: 
  562. case AMF_UNDEFINED: 
  563. case AMF_UNSUPPORTED: 
  564.       prop->p_type = AMF_NULL; 
  565. break; 
  566. case AMF_REFERENCE: 
  567.       { 
  568.     RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!"); 
  569. return -1; 
  570. break; 
  571.       } 
  572. case AMF_ECMA_ARRAY: 
  573.       { 
  574.     nSize -= 4; 
  575. /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
  576.     nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE); 
  577. if (nRes == -1) 
  578. return -1; 
  579.     nSize -= nRes; 
  580.     prop->p_type = AMF_OBJECT; 
  581. break; 
  582.       } 
  583. case AMF_OBJECT_END: 
  584.       { 
  585. return -1; 
  586. break; 
  587.       } 
  588. case AMF_STRICT_ARRAY: 
  589.       { 
  590.     unsigned int nArrayLen = AMF_DecodeInt32(pBuffer); 
  591.     nSize -= 4; 
  592.     nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize, 
  593.                    nArrayLen, FALSE); 
  594. if (nRes == -1) 
  595. return -1; 
  596.     nSize -= nRes; 
  597.     prop->p_type = AMF_OBJECT; 
  598. break; 
  599.       } 
  600. case AMF_DATE: 
  601.       { 
  602.     RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE"); 
  603. if (nSize < 10) 
  604. return -1; 
  605.     prop->p_vu.p_number = AMF_DecodeNumber(pBuffer); 
  606.     prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8); 
  607.     nSize -= 10; 
  608. break; 
  609.       } 
  610. case AMF_LONG_STRING: 
  611.       { 
  612.     unsigned int nStringSize = AMF_DecodeInt32(pBuffer); 
  613. if (nSize < (long)nStringSize + 4) 
  614. return -1; 
  615.     AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval); 
  616.     nSize -= (4 + nStringSize); 
  617.     prop->p_type = AMF_STRING; 
  618. break; 
  619.       } 
  620. case AMF_RECORDSET: 
  621.       { 
  622.     RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!"); 
  623. return -1; 
  624. break; 
  625.       } 
  626. case AMF_XML_DOC: 
  627.       { 
  628.     RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!"); 
  629. return -1; 
  630. break; 
  631.       } 
  632. case AMF_TYPED_OBJECT: 
  633.       { 
  634.     RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!"); 
  635. return -1; 
  636. break; 
  637.       } 
  638. case AMF_AVMPLUS: 
  639.       { 
  640. int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE); 
  641. if (nRes == -1) 
  642. return -1; 
  643.     nSize -= nRes; 
  644.     prop->p_type = AMF_OBJECT; 
  645. break; 
  646.       } 
  647. default: 
  648.       RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__, 
  649.       prop->p_type, pBuffer - 1); 
  650. return -1; 
  651.     } 
  652. return nOriginalSize - nSize; 
  653. void
  654. AMFProp_Dump(AMFObjectProperty *prop) 
  655. char strRes[256]; 
  656. char str[256]; 
  657.   AVal name; 
  658. if (prop->p_type == AMF_INVALID) 
  659.     { 
  660.       RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID"); 
  661. return; 
  662.     } 
  663. if (prop->p_type == AMF_NULL) 
  664.     { 
  665.       RTMP_Log(RTMP_LOGDEBUG, "Property: NULL"); 
  666. return; 
  667.     } 
  668. if (prop->p_name.av_len) 
  669.     { 
  670.       name = prop->p_name; 
  671.     } 
  672. else
  673.     { 
  674.       name.av_val = "no-name."; 
  675.       name.av_len = sizeof("no-name.") - 1; 
  676.     } 
  677. if (name.av_len > 18) 
  678.     name.av_len = 18; 
  679.   snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val); 
  680. if (prop->p_type == AMF_OBJECT) 
  681.     { 
  682.       RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes); 
  683.       AMF_Dump(&prop->p_vu.p_object); 
  684. return; 
  685.     } 
  686. switch (prop->p_type) 
  687.     { 
  688. case AMF_NUMBER: 
  689.       snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number); 
  690. break; 
  691. case AMF_BOOLEAN: 
  692.       snprintf(str, 255, "BOOLEAN:\t%s", 
  693.            prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE"); 
  694. break; 
  695. case AMF_STRING: 
  696.       snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len, 
  697.            prop->p_vu.p_aval.av_val); 
  698. break; 
  699. case AMF_DATE: 
  700.       snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d", 
  701.            prop->p_vu.p_number, prop->p_UTCoffset); 
  702. break; 
  703. default: 
  704.       snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type); 
  705.     } 
  706.   RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str); 
  707. void
  708. AMFProp_Reset(AMFObjectProperty *prop) 
  709. if (prop->p_type == AMF_OBJECT) 
  710.     AMF_Reset(&prop->p_vu.p_object); 
  711. else
  712.     { 
  713.       prop->p_vu.p_aval.av_len = 0; 
  714.       prop->p_vu.p_aval.av_val = NULL; 
  715.     } 
  716.   prop->p_type = AMF_INVALID; 
  717. /* AMFObject */
  718. char * 
  719. AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd) 
  720. int i; 
  721. if (pBuffer+4 >= pBufEnd) 
  722. return NULL; 
  723.   *pBuffer++ = AMF_OBJECT; 
  724. for (i = 0; i < obj->o_num; i++) 
  725.     { 
  726. char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd); 
  727. if (res == NULL) 
  728.     { 
  729.       RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d", 
  730.           i); 
  731. break; 
  732.     } 
  733. else
  734.     { 
  735.       pBuffer = res; 
  736.     } 
  737.     } 
  738. if (pBuffer + 3 >= pBufEnd) 
  739. return NULL;            /* no room for the end marker */
  740.   pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END); 
  741. return pBuffer; 
  742. int
  743. AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize, 
  744. int nArrayLen, int bDecodeName) 
  745. int nOriginalSize = nSize; 
  746. int bError = FALSE; 
  747.   obj->o_num = 0; 
  748.   obj->o_props = NULL; 
  749. while (nArrayLen > 0) 
  750.     { 
  751.       AMFObjectProperty prop; 
  752. int nRes; 
  753.       nArrayLen--; 
  754.       nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName); 
  755. if (nRes == -1) 
  756.     bError = TRUE; 
  757. else
  758.     { 
  759.       nSize -= nRes; 
  760.       pBuffer += nRes; 
  761.       AMF_AddProp(obj, &prop); 
  762.     } 
  763.     } 
  764. if (bError) 
  765. return -1; 
  766. return nOriginalSize - nSize; 
  767. int
  768. AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData) 
  769. int nOriginalSize = nSize; 
  770.   int32_t ref; 
  771. int len; 
  772.   obj->o_num = 0; 
  773.   obj->o_props = NULL; 
  774. if (bAMFData) 
  775.     { 
  776. if (*pBuffer != AMF3_OBJECT) 
  777.     RTMP_Log(RTMP_LOGERROR, 
  778. "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!"); 
  779.       pBuffer++; 
  780.       nSize--; 
  781.     } 
  782.   ref = 0; 
  783.   len = AMF3ReadInteger(pBuffer, &ref); 
  784.   pBuffer += len; 
  785.   nSize -= len; 
  786. if ((ref & 1) == 0) 
  787.     {               /* object reference, 0xxx */
  788.       uint32_t objectIndex = (ref >> 1); 
  789.       RTMP_Log(RTMP_LOGDEBUG, "Object reference, index: %d", objectIndex); 
  790.     } 
  791. else /* object instance */
  792.     { 
  793.       int32_t classRef = (ref >> 1); 
  794.       AMF3ClassDef cd = { {0, 0} 
  795.       }; 
  796.       AMFObjectProperty prop; 
  797. if ((classRef & 0x1) == 0) 
  798.     {           /* class reference */
  799.       uint32_t classIndex = (classRef >> 1); 
  800.       RTMP_Log(RTMP_LOGDEBUG, "Class reference: %d", classIndex); 
  801.     } 
  802. else
  803.     { 
  804.       int32_t classExtRef = (classRef >> 1); 
  805. int i; 
  806.       cd.cd_externalizable = (classExtRef & 0x1) == 1; 
  807.       cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1; 
  808.       cd.cd_num = classExtRef >> 2; 
  809. /* class name */
  810.       len = AMF3ReadString(pBuffer, &cd.cd_name); 
  811.       nSize -= len; 
  812.       pBuffer += len; 
  813. /*std::string str = className; */
  814.       RTMP_Log(RTMP_LOGDEBUG, 
  815. "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d", 
  816.           cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic, 
  817.           cd.cd_num); 
  818. for (i = 0; i < cd.cd_num; i++) 
  819.         { 
  820.           AVal memberName; 
  821.           len = AMF3ReadString(pBuffer, &memberName); 
  822.           RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val); 
  823.           AMF3CD_AddProp(&cd, &memberName); 
  824.           nSize -= len; 
  825.           pBuffer += len; 
  826.         } 
  827.     } 
  828. /* add as referencable object */
  829. if (cd.cd_externalizable) 
  830.     { 
  831. int nRes; 
  832.       AVal name = AVC("DEFAULT_ATTRIBUTE"); 
  833.       RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check"); 
  834.       nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE); 
  835. if (nRes == -1) 
  836.         RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!", 
  837.         __FUNCTION__); 
  838. else
  839.         { 
  840.           nSize -= nRes; 
  841.           pBuffer += nRes; 
  842.         } 
  843.       AMFProp_SetName(&prop, &name); 
  844.       AMF_AddProp(obj, &prop); 
  845.     } 
  846. else
  847.     { 
  848. int nRes, i; 
  849. for (i = 0; i < cd.cd_num; i++)    /* non-dynamic */
  850.         { 
  851.           nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE); 
  852. if (nRes == -1) 
  853.         RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!", 
  854.             __FUNCTION__); 
  855.           AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i)); 
  856.           AMF_AddProp(obj, &prop); 
  857.           pBuffer += nRes; 
  858.           nSize -= nRes; 
  859.         } 
  860. if (cd.cd_dynamic) 
  861.         { 
  862. int len = 0; 
  863. do
  864.         { 
  865.           nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE); 
  866.           AMF_AddProp(obj, &prop); 
  867.           pBuffer += nRes; 
  868.           nSize -= nRes; 
  869.           len = prop.p_name.av_len; 
  870.         } 
  871. while (len > 0); 
  872.         } 
  873.     } 
  874.       RTMP_Log(RTMP_LOGDEBUG, "class object!"); 
  875.     } 
  876. return nOriginalSize - nSize; 
  877. //解AMF编码的Object数据类型
  878. int
  879. AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName) 
  880. int nOriginalSize = nSize; 
  881. int bError = FALSE;       /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
  882.   obj->o_num = 0; 
  883.   obj->o_props = NULL; 
  884. while (nSize > 0) 
  885.     { 
  886.       AMFObjectProperty prop; 
  887. int nRes; 
  888. if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END) 
  889.     { 
  890.       nSize -= 3; 
  891.       bError = FALSE; 
  892. break; 
  893.     } 
  894. if (bError) 
  895.     { 
  896.       RTMP_Log(RTMP_LOGERROR, 
  897. "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!"); 
  898.       nSize--; 
  899.       pBuffer++; 
  900. continue; 
  901.     } 
  902. //解Object里的Property
  903.       nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName); 
  904. if (nRes == -1) 
  905.     bError = TRUE; 
  906. else
  907.     { 
  908.       nSize -= nRes; 
  909.       pBuffer += nRes; 
  910.       AMF_AddProp(obj, &prop); 
  911.     } 
  912.     } 
  913. if (bError) 
  914. return -1; 
  915. return nOriginalSize - nSize; 
  916. void
  917. AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop) 
  918. if (!(obj->o_num & 0x0f)) 
  919.     obj->o_props = (AMFObjectProperty *) 
  920.       realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty)); 
  921.   obj->o_props[obj->o_num++] = *prop; 
  922. int
  923. AMF_CountProp(AMFObject *obj) 
  924. return obj->o_num; 
  925. AMFObjectProperty * 
  926. AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex) 
  927. if (nIndex >= 0) 
  928.     { 
  929. if (nIndex <= obj->o_num) 
  930. return &obj->o_props[nIndex]; 
  931.     } 
  932. else
  933.     { 
  934. int n; 
  935. for (n = 0; n < obj->o_num; n++) 
  936.     { 
  937. if (AVMATCH(&obj->o_props[n].p_name, name)) 
  938. return &obj->o_props[n]; 
  939.     } 
  940.     } 
  941. return (AMFObjectProperty *)&AMFProp_Invalid; 
  942. void
  943. AMF_Dump(AMFObject *obj) 
  944. int n; 
  945.   RTMP_Log(RTMP_LOGDEBUG, "(object begin)"); 
  946. for (n = 0; n < obj->o_num; n++) 
  947.     { 
  948.       AMFProp_Dump(&obj->o_props[n]); 
  949.     } 
  950.   RTMP_Log(RTMP_LOGDEBUG, "(object end)"); 
  951. void
  952. AMF_Reset(AMFObject *obj) 
  953. int n; 
  954. for (n = 0; n < obj->o_num; n++) 
  955.     { 
  956.       AMFProp_Reset(&obj->o_props[n]); 
  957.     } 
  958.   free(obj->o_props); 
  959.   obj->o_props = NULL; 
  960.   obj->o_num = 0; 
  961. /* AMF3ClassDefinition */
  962. void
  963. AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop) 
  964. if (!(cd->cd_num & 0x0f)) 
  965.     cd->cd_props = (AVal *)realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal)); 
  966.   cd->cd_props[cd->cd_num++] = *prop; 
  967. AVal * 
  968. AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex) 
  969. if (nIndex >= cd->cd_num) 
  970. return (AVal *)&AV_empty; 
  971. return &cd->cd_props[nIndex]; 

可参考文件:

AMF3 中文版介绍:http://download.csdn.net/detail/leixiaohua1020/6389977

4: 连接第一步——握手(Hand Shake)

在这里分析一下RTMPdump(libRTMP)连接到支持RTMP协议的服务器的第一步:握手(Hand Shake)。

RTMP连接的过程曾经分析过:RTMP流媒体播放过程

在这里不再细说,分析一下位于handshake.h文件里面实现握手(HandShake)功能的函数:

注意:handshake.h里面代码量很大,但是很多代码都是为了处理RTMP的加密版协议的,例如rtmps;因此在这里就不做过多分析了,我们只考虑普通的RTMP协议。

[cpp] view plaincopy

  1. static int
  2. HandShake(RTMP * r, int FP9HandShake) 
  3. int i, offalg = 0; 
  4. int dhposClient = 0; 
  5. int digestPosClient = 0; 
  6. int encrypted = r->Link.protocol & RTMP_FEATURE_ENC; 
  7.   RC4_handle keyIn = 0; 
  8.   RC4_handle keyOut = 0; 
  9.   int32_t *ip; 
  10.   uint32_t uptime; 
  11.   uint8_t clientbuf[RTMP_SIG_SIZE + 4], *clientsig=clientbuf+4; 
  12.   uint8_t serversig[RTMP_SIG_SIZE], client2[RTMP_SIG_SIZE], *reply; 
  13.   uint8_t type; 
  14.   getoff *getdh = NULL, *getdig = NULL; 
  15. if (encrypted || r->Link.SWFSize) 
  16.     FP9HandShake = TRUE; 
  17. else
  18. //普通的
  19.     FP9HandShake = FALSE; 
  20.   r->Link.rc4keyIn = r->Link.rc4keyOut = 0; 
  21. if (encrypted) 
  22.     { 
  23.       clientsig[-1] = 0x06; /* 0x08 is RTMPE as well */
  24.       offalg = 1; 
  25.     } 
  26. else
  27. //0x03代表RTMP协议的版本(客户端要求的)
  28. //数组竟然能有“-1”下标
  29. //C0中的字段(1B)
  30.     clientsig[-1] = 0x03; 
  31.   uptime = htonl(RTMP_GetTime()); 
  32. //void *memcpy(void *dest, const void *src, int n);
  33. //由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内
  34. //把uptime的前4字节(其实一共就4字节)数据拷贝到clientsig指向的地址中
  35. //C1中的字段(4B)
  36.   memcpy(clientsig, &uptime, 4); 
  37. if (FP9HandShake) 
  38.     { 
  39. /* set version to at least 9.0.115.0 */
  40. if (encrypted) 
  41.     { 
  42.       clientsig[4] = 128; 
  43.       clientsig[6] = 3; 
  44.     } 
  45. else
  46.         { 
  47.       clientsig[4] = 10; 
  48.       clientsig[6] = 45; 
  49.     } 
  50.       clientsig[5] = 0; 
  51.       clientsig[7] = 2; 
  52.       RTMP_Log(RTMP_LOGDEBUG, "%s: Client type: %02X", __FUNCTION__, clientsig[-1]); 
  53.       getdig = digoff[offalg]; 
  54.       getdh  = dhoff[offalg]; 
  55.     } 
  56. else
  57.     { 
  58. //void *memset(void *s, int ch, size_t n);将s中前n个字节替换为ch并返回s;
  59. //将clientsig[4]开始的4个字节替换为0
  60. //这是C1的字段
  61.       memset(&clientsig[4], 0, 4); 
  62.     } 
  63. /* generate random data */
  64. #ifdef _DEBUG
  65. //将clientsig+8开始的1528个字节替换为0(这是一种简单的方法)
  66. //这是C1中的random字段
  67.   memset(clientsig+8, 0, RTMP_SIG_SIZE-8); 
  68. #else
  69. //实际中使用rand()循环生成1528字节的伪随机数
  70.   ip = (int32_t *)(clientsig+8); 
  71. for (i = 2; i < RTMP_SIG_SIZE/4; i++) 
  72.     *ip++ = rand(); 
  73. #endif
  74. /* set handshake digest */
  75. if (FP9HandShake) 
  76.     { 
  77. if (encrypted) 
  78.     { 
  79. /* generate Diffie-Hellmann parameters */
  80.       r->Link.dh = DHInit(1024); 
  81. if (!r->Link.dh) 
  82.         { 
  83.           RTMP_Log(RTMP_LOGERROR, "%s: Couldn‘t initialize Diffie-Hellmann!", 
  84.           __FUNCTION__); 
  85. return FALSE; 
  86.         } 
  87.       dhposClient = getdh(clientsig, RTMP_SIG_SIZE); 
  88.       RTMP_Log(RTMP_LOGDEBUG, "%s: DH pubkey position: %d", __FUNCTION__, dhposClient); 
  89. if (!DHGenerateKey((DH *)r->Link.dh)) 
  90.         { 
  91.           RTMP_Log(RTMP_LOGERROR, "%s: Couldn‘t generate Diffie-Hellmann public key!", 
  92.           __FUNCTION__); 
  93. return FALSE; 
  94.         } 
  95. if (!DHGetPublicKey((DH *)r->Link.dh, &clientsig[dhposClient], 128)) 
  96.         { 
  97.           RTMP_Log(RTMP_LOGERROR, "%s: Couldn‘t write public key!", __FUNCTION__); 
  98. return FALSE; 
  99.         } 
  100.     } 
  101.       digestPosClient = getdig(clientsig, RTMP_SIG_SIZE);   /* reuse this value in verification */
  102.       RTMP_Log(RTMP_LOGDEBUG, "%s: Client digest offset: %d", __FUNCTION__, 
  103.       digestPosClient); 
  104.       CalculateDigest(digestPosClient, clientsig, GenuineFPKey, 30, 
  105.               &clientsig[digestPosClient]); 
  106.       RTMP_Log(RTMP_LOGDEBUG, "%s: Initial client digest: ", __FUNCTION__); 
  107.       RTMP_LogHex(RTMP_LOGDEBUG, clientsig + digestPosClient, 
  108.          SHA256_DIGEST_LENGTH); 
  109.     } 
  110. #ifdef _DEBUG
  111.   RTMP_Log(RTMP_LOGDEBUG, "Clientsig: "); 
  112.   RTMP_LogHex(RTMP_LOGDEBUG, clientsig, RTMP_SIG_SIZE); 
  113. #endif
  114. //发送数据报C0+C1
  115. //从clientsig-1开始发,长度1536+1,两个包合并
  116. //握手----------------
  117.   r->dlg->AppendCInfo("建立连接:第1次连接。发送握手数据C0+C1"); 
  118. //-----------------------------
  119. if (!WriteN(r, (char *)clientsig-1, RTMP_SIG_SIZE + 1)) 
  120. return FALSE; 
  121. //读取数据报,长度1,存入type
  122. //是服务器的S0,表示服务器使用的RTMP版本
  123. if (ReadN(r, (char *)&type, 1) != 1)  /* 0x03 or 0x06 */
  124. return FALSE; 
  125. //握手----------------
  126.   r->dlg->AppendCInfo("建立连接:第1次连接。接收握手数据S0"); 
  127. //-----------------------------
  128.   RTMP_Log(RTMP_LOGDEBUG, "%s: Type Answer   : %02X", __FUNCTION__, type); 
  129. //客户端要求的版本和服务器提供的版本不同
  130. if (type != clientsig[-1]) 
  131.     RTMP_Log(RTMP_LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d", 
  132.     __FUNCTION__, clientsig[-1], type); 
  133. //握手----------------
  134.   r->dlg->AppendCInfo("建立连接:第1次连接。成功接收握手数据S0,服务器和客户端版本相同"); 
  135. //-----------------------------
  136. //客户端和服务端随机序列长度是否相同
  137. //握手----------------
  138.   r->dlg->AppendCInfo("建立连接:第1次连接。接收握手数据S1"); 
  139. //-----------------------------
  140. if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 
  141. return FALSE; 
  142. /* decode server response */
  143. //把serversig的前四个字节赋值给uptime
  144.   memcpy(&uptime, serversig, 4); 
  145. //大端转小端
  146.   uptime = ntohl(uptime); 
  147.   RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, uptime); 
  148.   RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version   : %d.%d.%d.%d", __FUNCTION__, serversig[4], 
  149.       serversig[5], serversig[6], serversig[7]); 
  150. if (FP9HandShake && type == 3 && !serversig[4]) 
  151.     FP9HandShake = FALSE; 
  152. #ifdef _DEBUG
  153.   RTMP_Log(RTMP_LOGDEBUG, "Server signature:"); 
  154.   RTMP_LogHex(RTMP_LOGDEBUG, serversig, RTMP_SIG_SIZE); 
  155. #endif
  156. if (FP9HandShake) 
  157.     { 
  158.       uint8_t digestResp[SHA256_DIGEST_LENGTH]; 
  159.       uint8_t *signatureResp = NULL; 
  160. /* we have to use this signature now to find the correct algorithms for getting the digest and DH positions */
  161. int digestPosServer = getdig(serversig, RTMP_SIG_SIZE); 
  162. if (!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36)) 
  163.     { 
  164.       RTMP_Log(RTMP_LOGWARNING, "Trying different position for server digest!"); 
  165.       offalg ^= 1; 
  166.       getdig = digoff[offalg]; 
  167.       getdh  = dhoff[offalg]; 
  168.       digestPosServer = getdig(serversig, RTMP_SIG_SIZE); 
  169. if (!VerifyDigest(digestPosServer, serversig, GenuineFMSKey, 36)) 
  170.         { 
  171.           RTMP_Log(RTMP_LOGERROR, "Couldn‘t verify the server digest"); /* continuing anyway will probably fail */
  172. return FALSE; 
  173.         } 
  174.     } 
  175. /* generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake) */
  176. if (r->Link.SWFSize) 
  177.     { 
  178. const char swfVerify[] = { 0x01, 0x01 }; 
  179. char *vend = r->Link.SWFVerificationResponse+sizeof(r->Link.SWFVerificationResponse); 
  180.       memcpy(r->Link.SWFVerificationResponse, swfVerify, 2); 
  181.       AMF_EncodeInt32(&r->Link.SWFVerificationResponse[2], vend, r->Link.SWFSize); 
  182.       AMF_EncodeInt32(&r->Link.SWFVerificationResponse[6], vend, r->Link.SWFSize); 
  183.       HMACsha256(r->Link.SWFHash, SHA256_DIGEST_LENGTH, 
  184.              &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], 
  185.              SHA256_DIGEST_LENGTH, 
  186.              (uint8_t *)&r->Link.SWFVerificationResponse[10]); 
  187.     } 
  188. /* do Diffie-Hellmann Key exchange for encrypted RTMP */
  189. if (encrypted) 
  190.     { 
  191. /* compute secret key */
  192.       uint8_t secretKey[128] = { 0 }; 
  193. int len, dhposServer; 
  194.       dhposServer = getdh(serversig, RTMP_SIG_SIZE); 
  195.       RTMP_Log(RTMP_LOGDEBUG, "%s: Server DH public key offset: %d", __FUNCTION__, 
  196.         dhposServer); 
  197.       len = DHComputeSharedSecretKey((DH *)r->Link.dh, &serversig[dhposServer], 
  198.                     128, secretKey); 
  199. if (len < 0) 
  200.         { 
  201.           RTMP_Log(RTMP_LOGDEBUG, "%s: Wrong secret key position!", __FUNCTION__); 
  202. return FALSE; 
  203.         } 
  204.       RTMP_Log(RTMP_LOGDEBUG, "%s: Secret key: ", __FUNCTION__); 
  205.       RTMP_LogHex(RTMP_LOGDEBUG, secretKey, 128); 
  206.       InitRC4Encryption(secretKey, 
  207.                 (uint8_t *) & serversig[dhposServer], 
  208.                 (uint8_t *) & clientsig[dhposClient], 
  209.                 &keyIn, &keyOut); 
  210.     } 
  211.       reply = client2; 
  212. #ifdef _DEBUG
  213.       memset(reply, 0xff, RTMP_SIG_SIZE); 
  214. #else
  215.       ip = (int32_t *)reply; 
  216. for (i = 0; i < RTMP_SIG_SIZE/4; i++) 
  217.         *ip++ = rand(); 
  218. #endif
  219. /* calculate response now */
  220.       signatureResp = reply+RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH; 
  221.       HMACsha256(&serversig[digestPosServer], SHA256_DIGEST_LENGTH, 
  222.          GenuineFPKey, sizeof(GenuineFPKey), digestResp); 
  223.       HMACsha256(reply, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digestResp, 
  224.          SHA256_DIGEST_LENGTH, signatureResp); 
  225. /* some info output */
  226.       RTMP_Log(RTMP_LOGDEBUG, 
  227. "%s: Calculated digest key from secure key and server digest: ", 
  228.       __FUNCTION__); 
  229.       RTMP_LogHex(RTMP_LOGDEBUG, digestResp, SHA256_DIGEST_LENGTH); 
  230. #ifdef FP10
  231. if (type == 8 ) 
  232.         { 
  233.       uint8_t *dptr = digestResp; 
  234.       uint8_t *sig = signatureResp; 
  235. /* encrypt signatureResp */
  236. for (i=0; i<SHA256_DIGEST_LENGTH; i+=8) 
  237.         rtmpe8_sig(sig+i, sig+i, dptr[i] % 15); 
  238.         } 
  239. #if 0
  240. else if (type == 9)) 
  241.         { 
  242.       uint8_t *dptr = digestResp; 
  243.       uint8_t *sig = signatureResp; 
  244. /* encrypt signatureResp */
  245. for (i=0; i<SHA256_DIGEST_LENGTH; i+=8) 
  246.             rtmpe9_sig(sig+i, sig+i, dptr[i] % 15); 
  247.         } 
  248. #endif
  249. #endif
  250.       RTMP_Log(RTMP_LOGDEBUG, "%s: Client signature calculated:", __FUNCTION__); 
  251.       RTMP_LogHex(RTMP_LOGDEBUG, signatureResp, SHA256_DIGEST_LENGTH); 
  252.     } 
  253. else
  254.     { 
  255. //直接赋值
  256.       reply = serversig; 
  257. #if 0
  258.       uptime = htonl(RTMP_GetTime()); 
  259.       memcpy(reply+4, &uptime, 4); 
  260. #endif
  261.     } 
  262. #ifdef _DEBUG
  263.   RTMP_Log(RTMP_LOGDEBUG, "%s: Sending handshake response: ", 
  264.     __FUNCTION__); 
  265.   RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE); 
  266. #endif
  267. //把reply中的1536字节数据发送出去
  268. //对应C2
  269. //握手----------------
  270.   r->dlg->AppendCInfo("建立连接:第1次连接。发送握手数据C2"); 
  271. //-----------------------------
  272. if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE)) 
  273. return FALSE; 
  274. /* 2nd part of handshake */
  275. //读取1536字节数据到serversig
  276. //握手----------------
  277.   r->dlg->AppendCInfo("建立连接:第1次连接。读取握手数据S2"); 
  278. //-----------------------------
  279. if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 
  280. return FALSE; 
  281. #ifdef _DEBUG
  282.   RTMP_Log(RTMP_LOGDEBUG, "%s: 2nd handshake: ", __FUNCTION__); 
  283.   RTMP_LogHex(RTMP_LOGDEBUG, serversig, RTMP_SIG_SIZE); 
  284. #endif
  285. if (FP9HandShake) 
  286.     { 
  287.       uint8_t signature[SHA256_DIGEST_LENGTH]; 
  288.       uint8_t digest[SHA256_DIGEST_LENGTH]; 
  289. if (serversig[4] == 0 && serversig[5] == 0 && serversig[6] == 0 
  290.       && serversig[7] == 0) 
  291.     { 
  292.       RTMP_Log(RTMP_LOGDEBUG, 
  293. "%s: Wait, did the server just refuse signed authentication?", 
  294.           __FUNCTION__); 
  295.     } 
  296.       RTMP_Log(RTMP_LOGDEBUG, "%s: Server sent signature:", __FUNCTION__); 
  297.       RTMP_LogHex(RTMP_LOGDEBUG, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], 
  298.          SHA256_DIGEST_LENGTH); 
  299. /* verify server response */
  300.       HMACsha256(&clientsig[digestPosClient], SHA256_DIGEST_LENGTH, 
  301.          GenuineFMSKey, sizeof(GenuineFMSKey), digest); 
  302.       HMACsha256(serversig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest, 
  303.          SHA256_DIGEST_LENGTH, signature); 
  304. /* show some information */
  305.       RTMP_Log(RTMP_LOGDEBUG, "%s: Digest key: ", __FUNCTION__); 
  306.       RTMP_LogHex(RTMP_LOGDEBUG, digest, SHA256_DIGEST_LENGTH); 
  307. #ifdef FP10
  308. if (type == 8 ) 
  309.         { 
  310.       uint8_t *dptr = digest; 
  311.       uint8_t *sig = signature; 
  312. /* encrypt signature */
  313. for (i=0; i<SHA256_DIGEST_LENGTH; i+=8) 
  314.         rtmpe8_sig(sig+i, sig+i, dptr[i] % 15); 
  315.         } 
  316. #if 0
  317. else if (type == 9) 
  318.         { 
  319.       uint8_t *dptr = digest; 
  320.       uint8_t *sig = signature; 
  321. /* encrypt signatureResp */
  322. for (i=0; i<SHA256_DIGEST_LENGTH; i+=8) 
  323.             rtmpe9_sig(sig+i, sig+i, dptr[i] % 15); 
  324.         } 
  325. #endif
  326. #endif
  327.       RTMP_Log(RTMP_LOGDEBUG, "%s: Signature calculated:", __FUNCTION__); 
  328.       RTMP_LogHex(RTMP_LOGDEBUG, signature, SHA256_DIGEST_LENGTH); 
  329. if (memcmp 
  330.       (signature, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], 
  331.        SHA256_DIGEST_LENGTH) != 0) 
  332.     { 
  333.       RTMP_Log(RTMP_LOGWARNING, "%s: Server not genuine Adobe!", __FUNCTION__); 
  334. return FALSE; 
  335.     } 
  336. else
  337.     { 
  338.       RTMP_Log(RTMP_LOGDEBUG, "%s: Genuine Adobe Flash Media Server", __FUNCTION__); 
  339.     } 
  340. if (encrypted) 
  341.     { 
  342. char buff[RTMP_SIG_SIZE]; 
  343. /* set keys for encryption from now on */
  344.       r->Link.rc4keyIn = keyIn; 
  345.       r->Link.rc4keyOut = keyOut; 
  346. /* update the keystreams */
  347. if (r->Link.rc4keyIn) 
  348.         { 
  349.           RC4_encrypt((RC4_KEY *)r->Link.rc4keyIn, RTMP_SIG_SIZE, (uint8_t *) buff); 
  350.         } 
  351. if (r->Link.rc4keyOut) 
  352.         { 
  353.           RC4_encrypt((RC4_KEY *)r->Link.rc4keyOut, RTMP_SIG_SIZE, (uint8_t *) buff); 
  354.         } 
  355.     } 
  356.     } 
  357. else
  358.     { 
  359. //int memcmp(const void *buf1, const void *buf2, unsigned int count); 当buf1=buf2时,返回值=0
  360. //比较serversig和clientsig是否相等
  361. //握手----------------
  362.         r->dlg->AppendCInfo("建立连接:第1次连接。比较握手数据签名"); 
  363. //-----------------------------
  364. if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0) 
  365.     { 
  366. //握手----------------
  367.         r->dlg->AppendCInfo("建立连接:第1次连接。握手数据签名不匹配!"); 
  368. //-----------------------------
  369.       RTMP_Log(RTMP_LOGWARNING, "%s: client signature does not match!", 
  370.           __FUNCTION__); 
  371.     } 
  372.     } 
  373. //握手----------------
  374.   r->dlg->AppendCInfo("建立连接:第1次连接。握手成功"); 
  375. //-----------------------------
  376.   RTMP_Log(RTMP_LOGDEBUG, "%s: Handshaking finished....", __FUNCTION__); 
  377. return TRUE; 

5: 建立一个流媒体连接 (NetConnection部分)

本篇文章分析一下RTMPdump里面的建立一个流媒体连接过程中的函数调用。

之前已经简单分析过流媒体链接的建立过程:

RTMP流媒体播放过程

而且分析过其函数调用过程:

RTMPDump源代码分析 0: 主要函数调用分析

在这里就不详细叙述了,其实主要是这两个函数:

RTMP_Connect()

RTMP_ConnectStream()

第一个函数用于建立RTMP中的NetConnection,第二个函数用于建立RTMP中的NetStream。一般是先调用第一个函数,然后调用第二个函数。

下面先来看看RTMP_Connect():

注意:贴上去的源代码是修改过的RTMPdump,我添加了输出信息的代码,形如:r->dlg->AppendCInfo("建立连接:第0次连接。开始建立Socket连接");改代码不影响程序运行,可忽略。

RTMP_Connect()

[cpp] view plaincopy

  1. //连接
  2. int
  3. RTMP_Connect(RTMP *r, RTMPPacket *cp) 
  4. //Socket结构体
  5. struct sockaddr_in service; 
  6. if (!r->Link.hostname.av_len) 
  7. return FALSE; 
  8.   memset(&service, 0, sizeof(struct sockaddr_in)); 
  9.   service.sin_family = AF_INET; 
  10. if (r->Link.socksport) 
  11.     { 
  12. //加入地址信息
  13. /* 使用SOCKS连接 */
  14. if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport)) 
  15. return FALSE; 
  16.     } 
  17. else
  18.     { 
  19. /* 直接连接 */
  20. if (!add_addr_info(&service, &r->Link.hostname, r->Link.port)) 
  21. return FALSE; 
  22.     } 
  23. //-----------------
  24.   r->dlg->AppendCInfo("建立连接:第0次连接。开始建立Socket连接"); 
  25. //-----------------------------
  26. if (!RTMP_Connect0(r, (struct sockaddr *)&service)){ 
  27.     r->dlg->AppendCInfo("建立连接:第0次连接。建立Socket连接失败"); 
  28. return FALSE; 
  29.   } 
  30. //-----------------
  31.   r->dlg->AppendCInfo("建立连接:第0次连接。建立Socket连接成功"); 
  32. //-----------------------------
  33.   r->m_bSendCounter = TRUE; 
  34. return RTMP_Connect1(r, cp); 

我们可以看出调用了两个函数RTMP_Connect0()以及RTMP_Connect1()。按照按先后顺序看看吧:

RTMP_Connect0()

[cpp] view plaincopy

  1. //sockaddr是Linux网络编程的地址结构体一种,其定义如下:
  2. //struct sockaddr{
  3. //  unsigned short sa_family;
  4. //  char sa_data[14];
  5. //};
  6. //说明:sa_family:是地址家族,也称作,协议族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET。
  7. //  sa_data:是14字节协议地址。
  8. //有时不使用sockaddr,而使用sockaddr_in(多用在windows)(等价)
  9. //struct sockaddr_in {
  10. //  short int sin_family;              /* Address family */
  11. //  unsigned short int sin_port;       /* Port number */
  12. //  struct in_addr sin_addr;           /* Internet address */
  13. //  unsigned char sin_zero[8];         /* Same size as struct sockaddr */
  14. //};
  15. //union {
  16. //   struct{
  17. //      unsigned char s_b1,s_b2,s_b3,s_b4;
  18. //         } S_un_b;
  19. //    struct {
  20. //      unsigned short s_w1,s_w2;
  21. //         } S_un_w;
  22. //    unsigned long S_addr;
  23. //   } S_un;
  24. //} in_addr;
  25. //第0次连接,建立Socket连接
  26. int
  27. RTMP_Connect0(RTMP *r, struct sockaddr * service) 
  28. int on = 1; 
  29.   r->m_sb.sb_timedout = FALSE; 
  30.   r->m_pausing = 0; 
  31.   r->m_fDuration = 0.0; 
  32. //创建一个Socket,并把Socket序号赋值给相应变量
  33. //-----------------
  34.   r->dlg->AppendCInfo("建立连接:第0次连接。create一个Socket"); 
  35. //-----------------------------
  36.   r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
  37. if (r->m_sb.sb_socket != -1) 
  38.     { 
  39. //定义函数 int connect (int sockfd,struct sockaddr * serv_addr,int addrlen); 
  40. //函数说明 connect()用来将参数sockfd 的Socket(刚刚创建)连至参数serv_addr
  41. //指定的网络地址。参数addrlen为sockaddr的结构长度。
  42. //连接
  43.     RTMP_LogPrintf("建立Socket连接!\n"); 
  44. //-----------------
  45.     r->dlg->AppendCInfo("建立连接:第0次连接。connect该Socket"); 
  46. //-----------------------------
  47. if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr)) < 0) 
  48.     { 
  49. //-----------------
  50.         r->dlg->AppendCInfo("建立连接:第0次连接。connect该Socket失败"); 
  51. //-----------------------------
  52. int err = GetSockError(); 
  53.       RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)", 
  54.           __FUNCTION__, err, strerror(err)); 
  55.       RTMP_Close(r); 
  56. return FALSE; 
  57.     } 
  58. //-----------------
  59.       r->dlg->AppendCInfo("建立连接:第0次连接。connect该Socket成功"); 
  60. //-----------------------------
  61. //指定了端口号。注:这不是必需的。
  62. if (r->Link.socksport) 
  63.     { 
  64.       RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__); 
  65. //谈判?发送数据报以进行谈判?!
  66. if (!SocksNegotiate(r)) 
  67.         { 
  68.           RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__); 
  69.           RTMP_Close(r); 
  70. return FALSE; 
  71.         } 
  72.     } 
  73.     } 
  74. else
  75.     { 
  76.       RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__, 
  77.       GetSockError()); 
  78. return FALSE; 
  79.     } 
  80. /* set timeout */
  81. //超时
  82.   { 
  83.     SET_RCVTIMEO(tv, r->Link.timeout); 
  84. if (setsockopt 
  85.         (r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) 
  86.       { 
  87.         RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!", 
  88.         __FUNCTION__, r->Link.timeout); 
  89.       } 
  90.   } 
  91.   setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)); 
  92. return TRUE; 

可见RTMP_Connect0()主要用于建立Socket连接,并未开始真正的建立RTMP连接。

再来看看RTMP_Connect1(),这是真正建立RTMP连接的函数:

RTMP_Connect1()

[cpp] view plaincopy

  1. //第1次连接,从握手开始
  2. int
  3. RTMP_Connect1(RTMP *r, RTMPPacket *cp) 
  4. if (r->Link.protocol & RTMP_FEATURE_SSL) 
  5.     { 
  6. #if defined(CRYPTO) && !defined(NO_SSL)
  7.       TLS_client(RTMP_TLS_ctx, r->m_sb.sb_ssl); 
  8.       TLS_setfd((SSL *)r->m_sb.sb_ssl, r->m_sb.sb_socket); 
  9. if (TLS_connect((SSL *)r->m_sb.sb_ssl) < 0) 
  10.     { 
  11.       RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__); 
  12.       RTMP_Close(r); 
  13. return FALSE; 
  14.     } 
  15. #else
  16.       RTMP_Log(RTMP_LOGERROR, "%s, no SSL/TLS support", __FUNCTION__); 
  17.       RTMP_Close(r); 
  18. return FALSE; 
  19. #endif
  20.     } 
  21. //使用HTTP
  22. if (r->Link.protocol & RTMP_FEATURE_HTTP) 
  23.     { 
  24.       r->m_msgCounter = 1; 
  25.       r->m_clientID.av_val = NULL; 
  26.       r->m_clientID.av_len = 0; 
  27.       HTTP_Post(r, RTMPT_OPEN, "", 1); 
  28.       HTTP_read(r, 1); 
  29.       r->m_msgCounter = 0; 
  30.     } 
  31.   RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__); 
  32. //握手----------------
  33.   r->dlg->AppendCInfo("建立连接:第1次连接。开始握手(HandShake)"); 
  34. //-----------------------------
  35.   RTMP_LogPrintf("开始握手(HandShake)!\n"); 
  36. if (!HandShake(r, TRUE)) 
  37.     { 
  38. //----------------
  39.         r->dlg->AppendCInfo("建立连接:第1次连接。握手(HandShake)失败!"); 
  40. //-----------------------------
  41.       RTMP_Log(RTMP_LOGERROR, "%s, handshake failed.", __FUNCTION__); 
  42.       RTMP_Close(r); 
  43. return FALSE; 
  44.     } 
  45. //----------------
  46.   r->dlg->AppendCInfo("建立连接:第1次连接。握手(HandShake)成功"); 
  47. //-----------------------------
  48.   RTMP_LogPrintf("握手(HandShake)完毕!\n"); 
  49.   RTMP_Log(RTMP_LOGDEBUG, "%s, handshaked", __FUNCTION__); 
  50. //发送“connect”命令--------------
  51. //----------------
  52.   r->dlg->AppendCInfo("建立连接:第1次连接。开始建立网络连接(NetConnection)"); 
  53. //-----------------------------
  54.   RTMP_LogPrintf("开始建立网络连接(NetConnection)!\n"); 
  55. //----------------
  56.   r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Connect)。"); 
  57. //-----------------------------
  58. if (!SendConnectPacket(r, cp)) 
  59.     { 
  60. //----------------
  61.         r->dlg->AppendCInfo("建立连接:第1次连接。建立网络连接(NetConnection)失败!"); 
  62. //-----------------------------
  63.       RTMP_Log(RTMP_LOGERROR, "%s, RTMP connect failed.", __FUNCTION__); 
  64.       RTMP_Close(r); 
  65. return FALSE; 
  66.     } 
  67. //----------------
  68.   r->dlg->AppendCInfo("建立连接:第1次连接。建立网络连接(NetConnection)成功"); 
  69. //-----------------------------
  70.   RTMP_LogPrintf("命令消息“Connect”发送完毕!\n"); 
  71. return TRUE; 

该函数做了以下事情:

HandShake()完成握手,之前已经分析过:RTMPdump 源代码分析 4: 连接第一步——握手(Hand Shake)

SendConnectPacket()发送包含“connect”命令的数据报,用于开始建立RTMP连接。具体该函数是怎么调用的,以后有机会再进行分析。

至此RTMP_Connect()分析完毕。

6: 建立一个流媒体连接 (NetStream部分 1)

前文已经分析了 RTMPdump中建立一个NetConnection的过程:RTMPdump 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)

多余的话不多说,下面先来看看RTMP_ConnectStream(),该函数主要用于在NetConnection基础上建立一个NetStream。

RTMP_ConnectStream()

[cpp] view plaincopy

  1. //创建流
  2. int
  3. RTMP_ConnectStream(RTMP *r, int seekTime) 
  4.   RTMPPacket packet = { 0 }; 
  5. /* seekTime was already set by SetupStream / SetupURL.
  6.    * This is only needed by ReconnectStream.
  7.    */
  8. if (seekTime > 0) 
  9.     r->Link.seekTime = seekTime; 
  10.   r->m_mediaChannel = 0; 
  11. while (!r->m_bPlaying && RTMP_IsConnected(r) && RTMP_ReadPacket(r, &packet)) 
  12.     { 
  13. if (RTMPPacket_IsReady(&packet)) 
  14.     { 
  15. if (!packet.m_nBodySize) 
  16. continue; 
  17. if ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) || 
  18.           (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) || 
  19.           (packet.m_packetType == RTMP_PACKET_TYPE_INFO)) 
  20.         { 
  21.           RTMP_Log(RTMP_LOGWARNING, "Received FLV packet before play()! Ignoring."); 
  22.           RTMPPacket_Free(&packet); 
  23. continue; 
  24.         } 
  25. //处理Packet!
  26. //----------------
  27.       r->dlg->AppendCInfo("建立网络流:处理收到的数据。开始处理收到的数据"); 
  28. //-----------------------------
  29.       RTMP_ClientPacket(r, &packet); 
  30. //----------------
  31.       r->dlg->AppendCInfo("建立网络流:处理收到的数据。处理完毕,清除数据。"); 
  32. //-----------------------------
  33.       RTMPPacket_Free(&packet); 
  34.     } 
  35.     } 
  36. return r->m_bPlaying; 

乍一看,这个函数的代码量好像挺少的,实际上不然,其复杂度还是挺高的。我觉得比RTMP_Connect()要复杂不少。

其关键就在于这个While()循环。首先,循环的三个条件都满足,就能进行循环。只有出错或者建立网络流(NetStream)的步骤完成后,才能跳出循环。

在这个函数中有两个函数尤为重要:

RTMP_ReadPacket()

RTMP_ClientPacket()

第一个函数的作用是读取通过Socket接收下来的消息(Message)包,但是不做任何处理。第二个函数则是处理消息(Message),并做出响应。这两个函数结合,就可以完成接收消息然后响应消息的步骤。

下面来开一下RTMP_ReadPacket():

[cpp] view plaincopy

  1. //读取收下来的Chunk
  2. int
  3. RTMP_ReadPacket(RTMP *r, RTMPPacket *packet) 
  4. //packet 存读取完后的的数据
  5. //Chunk Header最大值18
  6.   uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 }; 
  7. //header 指向的是从Socket中收下来的数据
  8. char *header = (char *)hbuf; 
  9. int nSize, hSize, nToRead, nChunk; 
  10. int didAlloc = FALSE; 
  11.   RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket); 
  12. //收下来的数据存入hbuf
  13. if (ReadN(r, (char *)hbuf, 1) == 0) 
  14.     { 
  15.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__); 
  16. return FALSE; 
  17.     } 
  18. //块类型fmt
  19.   packet->m_headerType = (hbuf[0] & 0xc0) >> 6; 
  20. //块流ID(2-63)
  21.   packet->m_nChannel = (hbuf[0] & 0x3f); 
  22.   header++; 
  23. //块流ID第1字节为0时,块流ID占2个字节
  24. if (packet->m_nChannel == 0) 
  25.     { 
  26. if (ReadN(r, (char *)&hbuf[1], 1) != 1) 
  27.     { 
  28.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 2nd byte", 
  29.           __FUNCTION__); 
  30. return FALSE; 
  31.     } 
  32. //计算块流ID(64-319)
  33.       packet->m_nChannel = hbuf[1]; 
  34.       packet->m_nChannel += 64; 
  35.       header++; 
  36.     } 
  37. //块流ID第1字节为0时,块流ID占3个字节
  38. else if (packet->m_nChannel == 1) 
  39.     { 
  40. int tmp; 
  41. if (ReadN(r, (char *)&hbuf[1], 2) != 2) 
  42.     { 
  43.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 3nd byte", 
  44.           __FUNCTION__); 
  45. return FALSE; 
  46.     } 
  47.       tmp = (hbuf[2] << 8) + hbuf[1]; 
  48. //计算块流ID(64-65599)
  49.       packet->m_nChannel = tmp + 64; 
  50.       RTMP_Log(RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel); 
  51.       header += 2; 
  52.     } 
  53. //ChunkHeader的大小(4种)
  54.   nSize = packetSize[packet->m_headerType]; 
  55. if (nSize == RTMP_LARGE_HEADER_SIZE)  /* if we get a full header the timestamp is absolute */
  56.     packet->m_hasAbsTimestamp = TRUE;    //11字节的完整ChunkMsgHeader的TimeStamp是绝对值
  57. else if (nSize < RTMP_LARGE_HEADER_SIZE) 
  58.     {               /* using values from the last message of this channel */
  59. if (r->m_vecChannelsIn[packet->m_nChannel]) 
  60.     memcpy(packet, r->m_vecChannelsIn[packet->m_nChannel], 
  61. sizeof(RTMPPacket)); 
  62.     } 
  63.   nSize--; 
  64. if (nSize > 0 && ReadN(r, header, nSize) != nSize) 
  65.     { 
  66.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header. type: %x", 
  67.       __FUNCTION__, (unsigned int)hbuf[0]); 
  68. return FALSE; 
  69.     } 
  70.   hSize = nSize + (header - (char *)hbuf); 
  71. if (nSize >= 3) 
  72.     { 
  73. //TimeStamp(注意 BigEndian to SmallEndian)(11,7,3字节首部都有)
  74.       packet->m_nTimeStamp = AMF_DecodeInt24(header); 
  75. /*RTMP_Log(RTMP_LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nTimeStamp, packet.m_hasAbsTimestamp); */
  76. //消息长度(11,7字节首部都有)
  77. if (nSize >= 6) 
  78.     { 
  79.       packet->m_nBodySize = AMF_DecodeInt24(header + 3); 
  80.       packet->m_nBytesRead = 0; 
  81.       RTMPPacket_Free(packet); 
  82. //(11,7字节首部都有)
  83. if (nSize > 6) 
  84.         { 
  85. //Msg type ID
  86.           packet->m_packetType = header[6]; 
  87. //Msg Stream ID
  88. if (nSize == 11) 
  89.         packet->m_nInfoField2 = DecodeInt32LE(header + 7); 
  90.         } 
  91.     } 
  92. //Extend TimeStamp
  93. if (packet->m_nTimeStamp == 0xffffff) 
  94.     { 
  95. if (ReadN(r, header + nSize, 4) != 4) 
  96.         { 
  97.           RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", 
  98.           __FUNCTION__); 
  99. return FALSE; 
  100.         } 
  101.       packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize); 
  102.       hSize += 4; 
  103.     } 
  104.     } 
  105.   RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize); 
  106. if (packet->m_nBodySize > 0 && packet->m_body == NULL) 
  107.     { 
  108. if (!RTMPPacket_Alloc(packet, packet->m_nBodySize)) 
  109.     { 
  110.       RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__); 
  111. return FALSE; 
  112.     } 
  113.       didAlloc = TRUE; 
  114.       packet->m_headerType = (hbuf[0] & 0xc0) >> 6; 
  115.     } 
  116.   nToRead = packet->m_nBodySize - packet->m_nBytesRead; 
  117.   nChunk = r->m_inChunkSize; 
  118. if (nToRead < nChunk) 
  119.     nChunk = nToRead; 
  120. /* Does the caller want the raw chunk? */
  121. if (packet->m_chunk) 
  122.     { 
  123.       packet->m_chunk->c_headerSize = hSize; 
  124.       memcpy(packet->m_chunk->c_header, hbuf, hSize); 
  125.       packet->m_chunk->c_chunk = packet->m_body + packet->m_nBytesRead; 
  126.       packet->m_chunk->c_chunkSize = nChunk; 
  127.     } 
  128. if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk) 
  129.     { 
  130.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %lu", 
  131.       __FUNCTION__, packet->m_nBodySize); 
  132. return FALSE; 
  133.     } 
  134.   RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)packet->m_body + packet->m_nBytesRead, nChunk); 
  135.   packet->m_nBytesRead += nChunk; 
  136. /* keep the packet as ref for other packets on this channel */
  137. if (!r->m_vecChannelsIn[packet->m_nChannel]) 
  138.     r->m_vecChannelsIn[packet->m_nChannel] = (RTMPPacket *) malloc(sizeof(RTMPPacket)); 
  139.   memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket)); 
  140. //读取完毕
  141. if (RTMPPacket_IsReady(packet)) 
  142.     { 
  143. /* make packet‘s timestamp absolute */
  144. if (!packet->m_hasAbsTimestamp) 
  145.     packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel]; /* timestamps seem to be always relative!! */
  146.       r->m_channelTimestamp[packet->m_nChannel] = packet->m_nTimeStamp; 
  147. /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
  148. /* arrives and requests to re-use some info (small packet header) */
  149.       r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL; 
  150.       r->m_vecChannelsIn[packet->m_nChannel]->m_nBytesRead = 0; 
  151.       r->m_vecChannelsIn[packet->m_nChannel]->m_hasAbsTimestamp = FALSE;   /* can only be false if we reuse header */
  152.     } 
  153. else
  154.     { 
  155.       packet->m_body = NULL; /* so it won‘t be erased on free */
  156.     } 
  157. return TRUE; 

在这里要注意的是,接收下来的实际上是块(Chunk)而不是消息(Message),因为消息(Message)在网络上传播的时候,实际上要分割成块(Chunk)。

这里解析的就是块(Chunk)

可参考:RTMP规范简单分析

具体的解析代码我就不多说了,直接参考RTMP协议规范就可以了,一个字节一个字节的解析就OK了。

7: 建立一个流媒体连接 (NetStream部分 2)

上回说到,有两个函数尤为重要:

RTMP_ReadPacket()

RTMP_ClientPacket()

而且分析了第一个函数。现在我们再来看看第二个函数吧。第二个函数的主要作用是:处理消息(Message),并做出响应。

先把带注释的代码贴上:

[cpp] view plaincopy

  1. //处理接收到的Chunk
  2. int
  3. RTMP_ClientPacket(RTMP *r, RTMPPacket *packet) 
  4. int bHasMediaPacket = 0; 
  5. switch (packet->m_packetType) 
  6.     { 
  7. //RTMP消息类型ID=1,设置块大小
  8. case 0x01: 
  9. /* chunk size */
  10. //----------------
  11.         r->dlg->AppendCInfo("处理收到的数据。消息 Set Chunk Size (typeID=1)。"); 
  12. //-----------------------------
  13.         RTMP_LogPrintf("处理消息 Set Chunk Size (typeID=1)\n"); 
  14.       HandleChangeChunkSize(r, packet); 
  15. break; 
  16. //RTMP消息类型ID=3,致谢
  17. case 0x03: 
  18. /* bytes read report */
  19.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); 
  20. break; 
  21. //RTMP消息类型ID=4,用户控制
  22. case 0x04: 
  23. /* ctrl */
  24. //----------------
  25.         r->dlg->AppendCInfo("处理收到的数据。消息 User Control (typeID=4)。"); 
  26. //-----------------------------
  27.         RTMP_LogPrintf("处理消息 User Control (typeID=4)\n"); 
  28.       HandleCtrl(r, packet); 
  29. break; 
  30. //RTMP消息类型ID=5
  31. case 0x05: 
  32. /* server bw */
  33. //----------------
  34.         r->dlg->AppendCInfo("处理收到的数据。消息 Window Acknowledgement Size (typeID=5)。"); 
  35. //-----------------------------
  36.         RTMP_LogPrintf("处理消息 Window Acknowledgement Size (typeID=5)\n"); 
  37.       HandleServerBW(r, packet); 
  38. break; 
  39. //RTMP消息类型ID=6
  40. case 0x06: 
  41. /* client bw */
  42. //----------------
  43.         r->dlg->AppendCInfo("处理收到的数据。消息 Set Peer Bandwidth (typeID=6)。"); 
  44. //-----------------------------
  45.         RTMP_LogPrintf("处理消息 Set Peer Bandwidth (typeID=6)\n"); 
  46.       HandleClientBW(r, packet); 
  47. break; 
  48. //RTMP消息类型ID=8,音频数据
  49. case 0x08: 
  50. /* audio data */
  51. /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
  52.       HandleAudio(r, packet); 
  53.       bHasMediaPacket = 1; 
  54. if (!r->m_mediaChannel) 
  55.     r->m_mediaChannel = packet->m_nChannel; 
  56. if (!r->m_pausing) 
  57.     r->m_mediaStamp = packet->m_nTimeStamp; 
  58. break; 
  59. //RTMP消息类型ID=9,视频数据
  60. case 0x09: 
  61. /* video data */
  62. /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
  63.       HandleVideo(r, packet); 
  64.       bHasMediaPacket = 1; 
  65. if (!r->m_mediaChannel) 
  66.     r->m_mediaChannel = packet->m_nChannel; 
  67. if (!r->m_pausing) 
  68.     r->m_mediaStamp = packet->m_nTimeStamp; 
  69. break; 
  70. //RTMP消息类型ID=15,AMF3编码,忽略
  71. case 0x0F:          /* flex stream send */
  72.       RTMP_Log(RTMP_LOGDEBUG, 
  73. "%s, flex stream send, size %lu bytes, not supported, ignoring", 
  74.       __FUNCTION__, packet->m_nBodySize); 
  75. break; 
  76. //RTMP消息类型ID=16,AMF3编码,忽略
  77. case 0x10:          /* flex shared object */
  78.       RTMP_Log(RTMP_LOGDEBUG, 
  79. "%s, flex shared object, size %lu bytes, not supported, ignoring", 
  80.       __FUNCTION__, packet->m_nBodySize); 
  81. break; 
  82. //RTMP消息类型ID=17,AMF3编码,忽略
  83. case 0x11:          /* flex message */
  84.       { 
  85.     RTMP_Log(RTMP_LOGDEBUG, 
  86. "%s, flex message, size %lu bytes, not fully supported", 
  87.         __FUNCTION__, packet->m_nBodySize); 
  88. /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  89. /* some DEBUG code */
  90. #if 0
  91.        RTMP_LIB_AMFObject obj; 
  92. int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1); 
  93. if(nRes < 0) { 
  94.        RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__); 
  95. /*return; */
  96.        } 
  97.        obj.Dump(); 
  98. #endif
  99. if (HandleInvoke(r, packet->m_body + 1, packet->m_nBodySize - 1) == 1) 
  100.       bHasMediaPacket = 2; 
  101. break; 
  102.       } 
  103. //RTMP消息类型ID=18,AMF0编码,数据消息
  104. case 0x12: 
  105. /* metadata (notify) */
  106.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__, 
  107.       packet->m_nBodySize); 
  108. //处理元数据,暂时注释
  109. /*
  110.       if (HandleMetadata(r, packet->m_body, packet->m_nBodySize))
  111.     bHasMediaPacket = 1;
  112.       break;
  113.       */
  114. //RTMP消息类型ID=19,AMF0编码,忽略
  115. case 0x13: 
  116.       RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring", 
  117.       __FUNCTION__); 
  118. break; 
  119. //RTMP消息类型ID=20,AMF0编码,命令消息
  120. //处理命令消息!
  121. case 0x14: 
  122. //----------------
  123.         r->dlg->AppendCInfo("处理收到的数据。消息 命令 (AMF0编码) (typeID=20)。"); 
  124. //-----------------------------
  125. /* invoke */
  126.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__, 
  127.       packet->m_nBodySize); 
  128.       RTMP_LogPrintf("处理命令消息 (typeID=20,AMF0编码)\n"); 
  129. /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  130. if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1) 
  131.     bHasMediaPacket = 2; 
  132. break; 
  133. //RTMP消息类型ID=22
  134. case 0x16: 
  135.       { 
  136. /* go through FLV packets and handle metadata packets */
  137.     unsigned int pos = 0; 
  138.     uint32_t nTimeStamp = packet->m_nTimeStamp; 
  139. while (pos + 11 < packet->m_nBodySize) 
  140.       { 
  141.         uint32_t dataSize = AMF_DecodeInt24(packet->m_body + pos + 1);   /* size without header (11) and prevTagSize (4) */
  142. if (pos + 11 + dataSize + 4 > packet->m_nBodySize) 
  143.           { 
  144.         RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!"); 
  145. break; 
  146.           } 
  147. if (packet->m_body[pos] == 0x12) 
  148.           { 
  149.         HandleMetadata(r, packet->m_body + pos + 11, dataSize); 
  150.           } 
  151. else if (packet->m_body[pos] == 8 || packet->m_body[pos] == 9) 
  152.           { 
  153.         nTimeStamp = AMF_DecodeInt24(packet->m_body + pos + 4); 
  154.         nTimeStamp |= (packet->m_body[pos + 7] << 24); 
  155.           } 
  156.         pos += (11 + dataSize + 4); 
  157.       } 
  158. if (!r->m_pausing) 
  159.       r->m_mediaStamp = nTimeStamp; 
  160. /* FLV tag(s) */
  161. /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
  162.     bHasMediaPacket = 1; 
  163. break; 
  164.       } 
  165. default: 
  166.       RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, 
  167.       packet->m_packetType); 
  168. #ifdef _DEBUG
  169.       RTMP_LogHex(RTMP_LOGDEBUG, (const uint8_t *)packet->m_body, packet->m_nBodySize); 
  170. #endif
  171.     } 
  172. return bHasMediaPacket; 

里面注释的比较多,可以看出,大体的思路是,根据接收到的消息(Message)类型的不同,做出不同的响应。例如收到的消息类型为0x01,那么就是设置块(Chunk)大小的协议,那么就调用相应的函数进行处理。

因此,本函数可以说是程序的灵魂,收到的各种命令消息都要经过本函数的判断决定调用哪个函数进行相应的处理。

在这里注意一下消息类型为0x14的消息,即消息类型ID为20的消息,是AMF0编码的命令消息。这在RTMP连接中是非常常见的,比如说各种控制命令:播放,暂停,停止等等。我们来仔细看看它的调用。

可以发现它调用了HandleInvoke()函数来处理服务器发来的AMF0编码的命令,来看看细节:

[cpp] view plaincopy

  1. /* Returns 0 for OK/Failed/error, 1 for ‘Stop or Complete‘ */
  2. static int
  3. HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) 
  4.   AMFObject obj; 
  5.   AVal method; 
  6. int txn; 
  7. int ret = 0, nRes; 
  8. if (body[0] != 0x02)      /* make sure it is a string method name we start with */
  9.     { 
  10.       RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", 
  11.       __FUNCTION__); 
  12. return 0; 
  13.     } 
  14.   nRes = AMF_Decode(&obj, body, nBodySize, FALSE); 
  15. if (nRes < 0) 
  16.     { 
  17.       RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); 
  18. return 0; 
  19.     } 
  20.   AMF_Dump(&obj); 
  21.   AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); 
  22.   txn = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1)); 
  23.   RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); 
  24. if (AVMATCH(&method, &av__result)) 
  25.     { 
  26.       AVal methodInvoked = {0}; 
  27. int i; 
  28. for (i=0; i<r->m_numCalls; i++) { 
  29. if (r->m_methodCalls[i].num == txn) { 
  30.       methodInvoked = r->m_methodCalls[i].name; 
  31.       AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE); 
  32. break; 
  33.     } 
  34.       } 
  35. if (!methodInvoked.av_val) { 
  36.         RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %d without matching request", 
  37.       __FUNCTION__, txn); 
  38. goto leave; 
  39.       } 
  40. //----------------
  41. char temp_str[100]; 
  42.       sprintf(temp_str,"接收数据。消息 %s 的 Result",methodInvoked.av_val); 
  43.       r->dlg->AppendCInfo(temp_str); 
  44. //-----------------------------
  45.       RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, 
  46.       methodInvoked.av_val); 
  47. if (AVMATCH(&methodInvoked, &av_connect)) 
  48.     { 
  49. //----------------
  50.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (Connect)"); 
  51. //-----------------------------
  52. if (r->Link.token.av_len) 
  53.         { 
  54.           AMFObjectProperty p; 
  55. if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p)) 
  56.         { 
  57.           DecodeTEA(&r->Link.token, &p.p_vu.p_aval); 
  58.           SendSecureTokenResponse(r, &p.p_vu.p_aval); 
  59.         } 
  60.         } 
  61. if (r->Link.protocol & RTMP_FEATURE_WRITE) 
  62.         { 
  63.           SendReleaseStream(r); 
  64.           SendFCPublish(r); 
  65.         } 
  66. else
  67.         { 
  68. //----------------
  69.             r->dlg->AppendCInfo("发送数据。消息 Window Acknowledgement Size (typeID=5)。"); 
  70. //-----------------------------
  71.             RTMP_LogPrintf("发送消息Window Acknowledgement Size(typeID=5)\n"); 
  72.           RTMP_SendServerBW(r); 
  73.           RTMP_SendCtrl(r, 3, 0, 300); 
  74.         } 
  75. //----------------
  76.       r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (CreateStream)。"); 
  77. //-----------------------------
  78.       RTMP_LogPrintf("发送命令消息“CreateStream” (typeID=20)\n"); 
  79.       RTMP_SendCreateStream(r); 
  80. if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) 
  81.         { 
  82. /* Send the FCSubscribe if live stream or if subscribepath is set */
  83. if (r->Link.subscribepath.av_len) 
  84.             SendFCSubscribe(r, &r->Link.subscribepath); 
  85. else if (r->Link.lFlags & RTMP_LF_LIVE) 
  86.             SendFCSubscribe(r, &r->Link.playpath); 
  87.         } 
  88.     } 
  89. else if (AVMATCH(&methodInvoked, &av_createStream)) 
  90.     { 
  91. //----------------
  92.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (CreateStream)"); 
  93. //-----------------------------
  94.       r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); 
  95. if (r->Link.protocol & RTMP_FEATURE_WRITE) 
  96.         { 
  97.           SendPublish(r); 
  98.         } 
  99. else
  100.         { 
  101. if (r->Link.lFlags & RTMP_LF_PLST) 
  102.             SendPlaylist(r); 
  103. //----------------
  104.           r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Play)。"); 
  105. //-----------------------------
  106.           RTMP_LogPrintf("发送命令消息“play” (typeID=20)\n"); 
  107.           SendPlay(r); 
  108.           RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS); 
  109.         } 
  110.     } 
  111. else if (AVMATCH(&methodInvoked, &av_play) || 
  112.         AVMATCH(&methodInvoked, &av_publish)) 
  113.     { 
  114. //----------------
  115.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (Play or Publish)"); 
  116. //-----------------------------
  117.       r->m_bPlaying = TRUE; 
  118.     } 
  119.       free(methodInvoked.av_val); 
  120.     } 
  121. else if (AVMATCH(&method, &av_onBWDone)) 
  122.     { 
  123. //----------------
  124.         r->dlg->AppendMLInfo(20,0,"命令消息","onBWDone"); 
  125. //-----------------------------
  126. if (!r->m_nBWCheckCounter) 
  127.         SendCheckBW(r); 
  128.     } 
  129. else if (AVMATCH(&method, &av_onFCSubscribe)) 
  130.     { 
  131. /* SendOnFCSubscribe(); */
  132.     } 
  133. else if (AVMATCH(&method, &av_onFCUnsubscribe)) 
  134.     { 
  135. //----------------
  136.         r->dlg->AppendMLInfo(20,0,"命令消息","onFCUnsubscribe"); 
  137. //-----------------------------
  138.       RTMP_Close(r); 
  139.       ret = 1; 
  140.     } 
  141. else if (AVMATCH(&method, &av_ping)) 
  142.     { 
  143. //----------------
  144.         r->dlg->AppendMLInfo(20,0,"命令消息","Ping"); 
  145. //-----------------------------
  146.       SendPong(r, txn); 
  147.     } 
  148. else if (AVMATCH(&method, &av__onbwcheck)) 
  149.     { 
  150. //----------------
  151.         r->dlg->AppendMLInfo(20,0,"命令消息","onBWcheck"); 
  152. //-----------------------------
  153.       SendCheckBWResult(r, txn); 
  154.     } 
  155. else if (AVMATCH(&method, &av__onbwdone)) 
  156.     { 
  157. //----------------
  158.         r->dlg->AppendMLInfo(20,0,"命令消息","onBWdone"); 
  159. //-----------------------------
  160. int i; 
  161. for (i = 0; i < r->m_numCalls; i++) 
  162. if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw)) 
  163.       { 
  164.         AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 
  165. break; 
  166.       } 
  167.     } 
  168. else if (AVMATCH(&method, &av__error)) 
  169.     { 
  170. //----------------
  171.         r->dlg->AppendMLInfo(20,0,"命令消息","error"); 
  172. //-----------------------------
  173.       RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); 
  174.     } 
  175. else if (AVMATCH(&method, &av_close)) 
  176.     { 
  177. //----------------
  178.         r->dlg->AppendMLInfo(20,0,"命令消息","close"); 
  179. //-----------------------------
  180.       RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); 
  181.       RTMP_Close(r); 
  182.     } 
  183. else if (AVMATCH(&method, &av_onStatus)) 
  184.     { 
  185. //----------------
  186.         r->dlg->AppendMLInfo(20,0,"命令消息","onStatus"); 
  187. //-----------------------------
  188.       AMFObject obj2; 
  189.       AVal code, level; 
  190.       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); 
  191.       AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code); 
  192.       AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level); 
  193.       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); 
  194. if (AVMATCH(&code, &av_NetStream_Failed) 
  195.       || AVMATCH(&code, &av_NetStream_Play_Failed) 
  196.       || AVMATCH(&code, &av_NetStream_Play_StreamNotFound) 
  197.       || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp)) 
  198.     { 
  199.       r->m_stream_id = -1; 
  200.       RTMP_Close(r); 
  201.       RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val); 
  202.     } 
  203. else if (AVMATCH(&code, &av_NetStream_Play_Start)) 
  204.     { 
  205. int i; 
  206.       r->m_bPlaying = TRUE; 
  207. for (i = 0; i < r->m_numCalls; i++) 
  208.         { 
  209. if (AVMATCH(&r->m_methodCalls[i].name, &av_play)) 
  210.         { 
  211.           AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 
  212. break; 
  213.         } 
  214.         } 
  215.     } 
  216. else if (AVMATCH(&code, &av_NetStream_Publish_Start)) 
  217.     { 
  218. int i; 
  219.       r->m_bPlaying = TRUE; 
  220. for (i = 0; i < r->m_numCalls; i++) 
  221.         { 
  222. if (AVMATCH(&r->m_methodCalls[i].name, &av_publish)) 
  223.         { 
  224.           AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 
  225. break; 
  226.         } 
  227.         } 
  228.     } 
  229. /* Return 1 if this is a Play.Complete or Play.Stop */
  230. else if (AVMATCH(&code, &av_NetStream_Play_Complete) 
  231.       || AVMATCH(&code, &av_NetStream_Play_Stop) 
  232.       || AVMATCH(&code, &av_NetStream_Play_UnpublishNotify)) 
  233.     { 
  234.       RTMP_Close(r); 
  235.       ret = 1; 
  236.     } 
  237. else if (AVMATCH(&code, &av_NetStream_Seek_Notify)) 
  238.         { 
  239.       r->m_read.flags &= ~RTMP_READ_SEEKING; 
  240.     } 
  241. else if (AVMATCH(&code, &av_NetStream_Pause_Notify)) 
  242.         { 
  243. if (r->m_pausing == 1 || r->m_pausing == 2) 
  244.       { 
  245.         RTMP_SendPause(r, FALSE, r->m_pauseStamp); 
  246.         r->m_pausing = 3; 
  247.       } 
  248.     } 
  249.     } 
  250. else if (AVMATCH(&method, &av_playlist_ready)) 
  251.     { 
  252. //----------------
  253.         r->dlg->AppendMLInfo(20,0,"命令消息","playlist_ready"); 
  254. //-----------------------------
  255. int i; 
  256. for (i = 0; i < r->m_numCalls; i++) 
  257.         { 
  258. if (AVMATCH(&r->m_methodCalls[i].name, &av_set_playlist)) 
  259.         { 
  260.           AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 
  261. break; 
  262.         } 
  263.         } 
  264.     } 
  265. else
  266.     { 
  267.     } 
  268. leave: 
  269.   AMF_Reset(&obj); 
  270. return ret; 
  271. int
  272. RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name, 
  273.                    AMFObjectProperty * p) 
  274. int n; 
  275. /* this is a small object search to locate the "duration" property */
  276. for (n = 0; n < obj->o_num; n++) 
  277.     { 
  278.       AMFObjectProperty *prop = AMF_GetProp(obj, NULL, n); 
  279. if (AVMATCH(&prop->p_name, name)) 
  280.     { 
  281.       *p = *prop; 
  282. return TRUE; 
  283.     } 
  284. if (prop->p_type == AMF_OBJECT) 
  285.     { 
  286. if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p)) 
  287. return TRUE; 
  288.     } 
  289.     } 
  290. return FALSE; 

该函数主要做了以下几步:

1.调用AMF_Decode()解码AMF命令数据

2.调用AMFProp_GetString()获取具体命令的字符串

3.调用AVMATCH()比较字符串,不同的命令做不同的处理,例如以下几个:

[cpp] view plaincopy

  1. AVMATCH(&methodInvoked, &av_connect) 
  2. AVMATCH(&methodInvoked, &av_createStream) 
  3. AVMATCH(&methodInvoked, &av_play) 
  4. AVMATCH(&methodInvoked, &av_publish) 
  5. AVMATCH(&method, &av_onBWDone) 

等等,不一一例举了

具体的处理过程如下所示。在这里说一个“建立网络流”(createStream)的例子,通常发生在建立网络连接(NetConnection)之后,播放(Play)之前。

[cpp] view plaincopy

  1. else if (AVMATCH(&methodInvoked, &av_createStream)) 
  2.     { 
  3. //----------------
  4.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (CreateStream)"); 
  5. //-----------------------------
  6.       r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); 
  7. if (r->Link.protocol & RTMP_FEATURE_WRITE) 
  8.         { 
  9.           SendPublish(r); 
  10.         } 
  11. else
  12.         { 
  13. if (r->Link.lFlags & RTMP_LF_PLST) 
  14.             SendPlaylist(r); 
  15. //----------------
  16.           r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Play)。"); 
  17. //-----------------------------
  18.           RTMP_LogPrintf("发送命令消息“play” (typeID=20)\n"); 
  19.           SendPlay(r); 
  20.           RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS); 
  21.         } 
  22.     } 

由代码可见,程序先获取了stream_id,然后发送了两个消息(Message),分别是SendPlaylist()和SendPlay(),用于获取播放列表,以及开始播放流媒体数据。

8: 发送消息(Message)

之前写了一系列的文章介绍RTMPDump各种函数。比如怎么建立网络连接(NetConnection),怎么建立网络流(NetStream)之类的,唯独没有介绍这些发送或接收的数据,在底层到底是怎么实现的。本文就是要剖析一下其内部的实现。即这些消息(Message)到底是怎么发送和接收的。

先来看看发送消息吧。

发送connect命令使用函数SendConnectPacket()

发送createstream命令使用RTMP_SendCreateStream()

发送realeaseStream命令使用SendReleaseStream()

发送publish命令使用SendPublish()

发送deleteStream的命令使用SendDeleteStream()

发送pause命令使用RTMP_SendPause()

不再一一例举,发现函数命名有两种规律:RTMP_Send***()或者Send***(),其中*号代表命令的名称。

SendConnectPacket()这个命令是每次程序开始运行的时候发送的第一个命令消息,内容比较多,包含了很多AMF编码的内容,在此不多做分析,贴上代码:

[cpp] view plaincopy

  1. //发送“connect”命令
  2. static int
  3. SendConnectPacket(RTMP *r, RTMPPacket *cp) 
  4.   RTMPPacket packet; 
  5. char pbuf[4096], *pend = pbuf + sizeof(pbuf); 
  6. char *enc; 
  7. if (cp) 
  8. return RTMP_SendPacket(r, cp, TRUE); 
  9.   packet.m_nChannel = 0x03; /* control channel (invoke) */
  10.   packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 
  11.   packet.m_packetType = 0x14;   /* INVOKE */
  12.   packet.m_nTimeStamp = 0; 
  13.   packet.m_nInfoField2 = 0; 
  14.   packet.m_hasAbsTimestamp = 0; 
  15.   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 
  16.   enc = packet.m_body; 
  17.   enc = AMF_EncodeString(enc, pend, &av_connect); 
  18.   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 
  19.   *enc++ = AMF_OBJECT; 
  20.   enc = AMF_EncodeNamedString(enc, pend, &av_app, &r->Link.app); 
  21. if (!enc) 
  22. return FALSE; 
  23. if (r->Link.protocol & RTMP_FEATURE_WRITE) 
  24.     { 
  25.       enc = AMF_EncodeNamedString(enc, pend, &av_type, &av_nonprivate); 
  26. if (!enc) 
  27. return FALSE; 
  28.     } 
  29. if (r->Link.flashVer.av_len) 
  30.     { 
  31.       enc = AMF_EncodeNamedString(enc, pend, &av_flashVer, &r->Link.flashVer); 
  32. if (!enc) 
  33. return FALSE; 
  34.     } 
  35. if (r->Link.swfUrl.av_len) 
  36.     { 
  37.       enc = AMF_EncodeNamedString(enc, pend, &av_swfUrl, &r->Link.swfUrl); 
  38. if (!enc) 
  39. return FALSE; 
  40.     } 
  41. if (r->Link.tcUrl.av_len) 
  42.     { 
  43.       enc = AMF_EncodeNamedString(enc, pend, &av_tcUrl, &r->Link.tcUrl); 
  44. if (!enc) 
  45. return FALSE; 
  46.     } 
  47. if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) 
  48.     { 
  49.       enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE); 
  50. if (!enc) 
  51. return FALSE; 
  52.       enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0); 
  53. if (!enc) 
  54. return FALSE; 
  55.       enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs); 
  56. if (!enc) 
  57. return FALSE; 
  58.       enc = AMF_EncodeNamedNumber(enc, pend, &av_videoCodecs, r->m_fVideoCodecs); 
  59. if (!enc) 
  60. return FALSE; 
  61.       enc = AMF_EncodeNamedNumber(enc, pend, &av_videoFunction, 1.0); 
  62. if (!enc) 
  63. return FALSE; 
  64. if (r->Link.pageUrl.av_len) 
  65.     { 
  66.       enc = AMF_EncodeNamedString(enc, pend, &av_pageUrl, &r->Link.pageUrl); 
  67. if (!enc) 
  68. return FALSE; 
  69.     } 
  70.     } 
  71. if (r->m_fEncoding != 0.0 || r->m_bSendEncoding) 
  72.     {   /* AMF0, AMF3 not fully supported yet */
  73.       enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding); 
  74. if (!enc) 
  75. return FALSE; 
  76.     } 
  77. if (enc + 3 >= pend) 
  78. return FALSE; 
  79.   *enc++ = 0; 
  80.   *enc++ = 0;           /* end of object - 0x00 0x00 0x09 */
  81.   *enc++ = AMF_OBJECT_END; 
  82. /* add auth string */
  83. if (r->Link.auth.av_len) 
  84.     { 
  85.       enc = AMF_EncodeBoolean(enc, pend, r->Link.lFlags & RTMP_LF_AUTH); 
  86. if (!enc) 
  87. return FALSE; 
  88.       enc = AMF_EncodeString(enc, pend, &r->Link.auth); 
  89. if (!enc) 
  90. return FALSE; 
  91.     } 
  92. if (r->Link.extras.o_num) 
  93.     { 
  94. int i; 
  95. for (i = 0; i < r->Link.extras.o_num; i++) 
  96.     { 
  97.       enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend); 
  98. if (!enc) 
  99. return FALSE; 
  100.     } 
  101.     } 
  102.   packet.m_nBodySize = enc - packet.m_body; 
  103. //----------------
  104.   r->dlg->AppendMLInfo(20,1,"命令消息","Connect"); 
  105. //-----------------------------
  106. return RTMP_SendPacket(r, &packet, TRUE); 

RTMP_SendCreateStream()命令相对而言比较简单,代码如下:

[cpp] view plaincopy

  1. //发送“createstream”命令
  2. int
  3. RTMP_SendCreateStream(RTMP *r) 
  4.   RTMPPacket packet; 
  5. char pbuf[256], *pend = pbuf + sizeof(pbuf); 
  6. char *enc; 
  7.   packet.m_nChannel = 0x03; /* control channel (invoke) */
  8.   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 
  9.   packet.m_packetType = 0x14;   /* INVOKE */
  10.   packet.m_nTimeStamp = 0; 
  11.   packet.m_nInfoField2 = 0; 
  12.   packet.m_hasAbsTimestamp = 0; 
  13.   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 
  14.   enc = packet.m_body; 
  15.   enc = AMF_EncodeString(enc, pend, &av_createStream); 
  16.   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 
  17.   *enc++ = AMF_NULL;        /* NULL */
  18.   packet.m_nBodySize = enc - packet.m_body; 
  19. //----------------
  20.   r->dlg->AppendMLInfo(20,1,"命令消息","CreateStream"); 
  21. //-----------------------------
  22. return RTMP_SendPacket(r, &packet, TRUE); 

同样,SendReleaseStream()内容也比较简单,我对其中部分内容作了注释:

[cpp] view plaincopy

  1. //发送RealeaseStream命令
  2. static int
  3. SendReleaseStream(RTMP *r) 
  4.   RTMPPacket packet; 
  5. char pbuf[1024], *pend = pbuf + sizeof(pbuf); 
  6. char *enc; 
  7.   packet.m_nChannel = 0x03; /* control channel (invoke) */
  8.   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 
  9.   packet.m_packetType = 0x14;   /* INVOKE */
  10.   packet.m_nTimeStamp = 0; 
  11.   packet.m_nInfoField2 = 0; 
  12.   packet.m_hasAbsTimestamp = 0; 
  13.   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 
  14. enc = packet.m_body; 
  15. //对“releaseStream”字符串进行AMF编码
  16.   enc = AMF_EncodeString(enc, pend, &av_releaseStream); 
  17. //对传输ID(0)进行AMF编码?
  18.   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 
  19. //命令对象
  20.   *enc++ = AMF_NULL; 
  21. //对播放路径字符串进行AMF编码
  22.   enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 
  23. if (!enc) 
  24. return FALSE; 
  25.   packet.m_nBodySize = enc - packet.m_body; 
  26. //----------------
  27.   r->dlg->AppendMLInfo(20,1,"命令消息","ReleaseStream"); 
  28. //-----------------------------
  29. return RTMP_SendPacket(r, &packet, FALSE); 

再来看一个SendPublish()函数,用于发送“publish”命令

[cpp] view plaincopy

  1. //发送Publish命令
  2. static int
  3. SendPublish(RTMP *r) 
  4.   RTMPPacket packet; 
  5. char pbuf[1024], *pend = pbuf + sizeof(pbuf); 
  6. char *enc; 
  7. //块流ID为4
  8.   packet.m_nChannel = 0x04; /* source channel (invoke) */
  9.   packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 
  10. //命令消息,类型20
  11.   packet.m_packetType = 0x14;   /* INVOKE */
  12.   packet.m_nTimeStamp = 0; 
  13. //流ID
  14.   packet.m_nInfoField2 = r->m_stream_id; 
  15.   packet.m_hasAbsTimestamp = 0; 
  16.   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 
  17. //指向Chunk的负载
  18.   enc = packet.m_body; 
  19. //对“publish”字符串进行AMF编码
  20.   enc = AMF_EncodeString(enc, pend, &av_publish); 
  21.   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 
  22. //命令对象为空
  23.   *enc++ = AMF_NULL; 
  24.   enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 
  25. if (!enc) 
  26. return FALSE; 
  27. /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
  28.   enc = AMF_EncodeString(enc, pend, &av_live); 
  29. if (!enc) 
  30. return FALSE; 
  31.   packet.m_nBodySize = enc - packet.m_body; 
  32. //----------------
  33.   r->dlg->AppendMLInfo(20,1,"命令消息","Pulish"); 
  34. //-----------------------------
  35. return RTMP_SendPacket(r, &packet, TRUE); 

其他的命令不再一一例举,总体的思路是声明一个RTMPPacket类型的结构体,然后设置各种属性值,最后交给RTMP_SendPacket()进行发送。

RTMPPacket类型的结构体定义如下,一个RTMPPacket对应RTMP协议规范里面的一个块(Chunk)。

[cpp] view plaincopy

  1. //Chunk信息
  2. typedef struct RTMPPacket 
  3.   { 
  4.     uint8_t m_headerType;//ChunkMsgHeader的类型(4种)
  5.     uint8_t m_packetType;//Message type ID(1-7协议控制;8,9音视频;10以后为AMF编码消息)
  6.     uint8_t m_hasAbsTimestamp;  /* Timestamp 是绝对值还是相对值? */
  7. int m_nChannel;         //块流ID
  8.     uint32_t m_nTimeStamp;  // Timestamp
  9.     int32_t m_nInfoField2;  /* last 4 bytes in a long header,消息流ID */
  10.     uint32_t m_nBodySize;   //消息长度
  11.     uint32_t m_nBytesRead; 
  12.     RTMPChunk *m_chunk; 
  13. char *m_body; 
  14.   } RTMPPacket; 

下面我们来看看RTMP_SendPacket()吧,各种的RTMPPacket(即各种Chunk)都需要用这个函数进行发送。

[cpp] view plaincopy

  1. //自己编一个数据报发送出去!
  2. //非常常用
  3. int
  4. RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue) 
  5. const RTMPPacket *prevPacket = r->m_vecChannelsOut[packet->m_nChannel]; 
  6.   uint32_t last = 0; 
  7. int nSize; 
  8. int hSize, cSize; 
  9. char *header, *hptr, *hend, hbuf[RTMP_MAX_HEADER_SIZE], c; 
  10.   uint32_t t; 
  11. char *buffer, *tbuf = NULL, *toff = NULL; 
  12. int nChunkSize; 
  13. int tlen; 
  14. //不是完整ChunkMsgHeader
  15. if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE) 
  16.     { 
  17. /* compress a bit by using the prev packet‘s attributes */
  18. //获取ChunkMsgHeader的类型
  19. //前一个Chunk和这个Chunk对比
  20. if (prevPacket->m_nBodySize == packet->m_nBodySize 
  21.       && prevPacket->m_packetType == packet->m_packetType 
  22.       && packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM) 
  23.     packet->m_headerType = RTMP_PACKET_SIZE_SMALL; 
  24. if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp 
  25.       && packet->m_headerType == RTMP_PACKET_SIZE_SMALL) 
  26.     packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM; 
  27. //上一个packet的TimeStamp
  28.       last = prevPacket->m_nTimeStamp; 
  29.     } 
  30. if (packet->m_headerType > 3)   /* sanity */
  31.     { 
  32.       RTMP_Log(RTMP_LOGERROR, "sanity failed!! trying to send header of type: 0x%02x.", 
  33.       (unsigned char)packet->m_headerType); 
  34. return FALSE; 
  35.     } 
  36. //chunk包头大小;packetSize[] = { 12, 8, 4, 1 }
  37.   nSize = packetSize[packet->m_headerType]; 
  38.   hSize = nSize; cSize = 0; 
  39. //相对的TimeStamp
  40.   t = packet->m_nTimeStamp - last; 
  41. if (packet->m_body) 
  42.     { 
  43. //Header的Start
  44. //m_body是指向负载数据首地址的指针;“-”号用于指针前移
  45.       header = packet->m_body - nSize; 
  46. //Header的End
  47.       hend = packet->m_body; 
  48.     } 
  49. else
  50.     { 
  51.       header = hbuf + 6; 
  52.       hend = hbuf + sizeof(hbuf); 
  53.     } 
  54. //当ChunkStreamID大于319时
  55. if (packet->m_nChannel > 319) 
  56. //ChunkBasicHeader是3个字节
  57.     cSize = 2; 
  58. //当ChunkStreamID大于63时
  59. else if (packet->m_nChannel > 63) 
  60. //ChunkBasicHeader是2个字节
  61.     cSize = 1; 
  62. if (cSize) 
  63.     { 
  64. //header指针指向ChunkMsgHeader
  65.       header -= cSize; 
  66. //hsize加上ChunkBasicHeader的长度
  67.       hSize += cSize; 
  68.     } 
  69. //相对TimeStamp大于0xffffff,此时需要使用ExtendTimeStamp
  70. if (nSize > 1 && t >= 0xffffff) 
  71.     { 
  72.       header -= 4; 
  73.       hSize += 4; 
  74.     } 
  75.   hptr = header; 
  76. //把ChunkBasicHeader的Fmt类型左移6位
  77.   c = packet->m_headerType << 6; 
  78. switch (cSize) 
  79.     { 
  80. //把ChunkBasicHeader的低6位设置成ChunkStreamID
  81. case 0: 
  82.       c |= packet->m_nChannel; 
  83. break; 
  84. //同理,但低6位设置成000000
  85. case 1: 
  86. break; 
  87. //同理,但低6位设置成000001
  88. case 2: 
  89.       c |= 1; 
  90. break; 
  91.     } 
  92. //可以拆分成两句*hptr=c;hptr++,此时hptr指向第2个字节
  93.   *hptr++ = c; 
  94. //CSize>0,即ChunkBasicHeader大于1字节
  95. if (cSize) 
  96.     { 
  97. //将要放到第2字节的内容tmp
  98. int tmp = packet->m_nChannel - 64; 
  99. //获取低位存储与第2字节
  100.       *hptr++ = tmp & 0xff; 
  101. //ChunkBasicHeader是最大的3字节时
  102. if (cSize == 2) 
  103. //获取高位存储于最后1个字节(注意:排序使用大端序列,和主机相反)
  104.     *hptr++ = tmp >> 8; 
  105.     } 
  106. //ChunkMsgHeader。注意一共有4种,包含的字段数不同。
  107. //TimeStamp(3B)
  108. if (nSize > 1) 
  109.     { 
  110. //相对TimeStamp和绝对TimeStamp?
  111.       hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t); 
  112.     } 
  113. //MessageLength+MessageTypeID(4B)
  114. if (nSize > 4) 
  115.     { 
  116. //MessageLength
  117.       hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize); 
  118. //MessageTypeID
  119.       *hptr++ = packet->m_packetType; 
  120.     } 
  121. //MessageStreamID(4B)
  122. if (nSize > 8) 
  123.     hptr += EncodeInt32LE(hptr, packet->m_nInfoField2); 
  124. //ExtendedTimeStamp
  125. if (nSize > 1 && t >= 0xffffff) 
  126.     hptr = AMF_EncodeInt32(hptr, hend, t); 
  127. //负载长度,指向负载的指针
  128.   nSize = packet->m_nBodySize; 
  129.   buffer = packet->m_body; 
  130. //Chunk大小,默认128字节
  131.   nChunkSize = r->m_outChunkSize; 
  132.   RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, r->m_sb.sb_socket, 
  133.       nSize); 
  134. /* send all chunks in one HTTP request */
  135. //使用HTTP
  136. if (r->Link.protocol & RTMP_FEATURE_HTTP) 
  137.     { 
  138. //nSize:Message负载长度;nChunkSize:Chunk长度;
  139. //例nSize:307,nChunkSize:128;
  140. //可分为(307+128-1)/128=3个
  141. //为什么+nChunkSize-1?因为除法会只取整数部分!
  142. int chunks = (nSize+nChunkSize-1) / nChunkSize; 
  143. //Chunk个数超过一个
  144. if (chunks > 1) 
  145.         { 
  146. //注意:CSize=1表示ChunkBasicHeader是2字节
  147. //消息分n块后总的开销:
  148. //n个ChunkBasicHeader,1个ChunkMsgHeader,1个Message负载
  149. //实际中只有第一个Chunk是完整的,剩下的只有ChunkBasicHeader
  150.       tlen = chunks * (cSize + 1) + nSize + hSize; 
  151. //分配内存
  152.       tbuf = (char *) malloc(tlen); 
  153. if (!tbuf) 
  154. return FALSE; 
  155.       toff = tbuf; 
  156.     } 
  157. //消息的负载+头
  158.     } 
  159. while (nSize + hSize) 
  160.     { 
  161. int wrote; 
  162. //消息负载<Chunk大小(不用分块)
  163. if (nSize < nChunkSize) 
  164. //Chunk可能小于设定值
  165.     nChunkSize = nSize; 
  166.       RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)header, hSize); 
  167.       RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)buffer, nChunkSize); 
  168. if (tbuf) 
  169.         { 
  170. //void *memcpy(void *dest, const void *src, int n);
  171. //由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内
  172.       memcpy(toff, header, nChunkSize + hSize); 
  173.       toff += nChunkSize + hSize; 
  174.     } 
  175. else
  176.         { 
  177.       wrote = WriteN(r, header, nChunkSize + hSize); 
  178. if (!wrote) 
  179. return FALSE; 
  180.     } 
  181. //消息负载长度-Chunk负载长度
  182.       nSize -= nChunkSize; 
  183. //Buffer指针前移1个Chunk负载长度
  184.       buffer += nChunkSize; 
  185.       hSize = 0; 
  186. //如果消息没有发完
  187. if (nSize > 0) 
  188.     { 
  189. //ChunkBasicHeader
  190.       header = buffer - 1; 
  191.       hSize = 1; 
  192. if (cSize) 
  193.         { 
  194.           header -= cSize; 
  195.           hSize += cSize; 
  196.         } 
  197. //ChunkBasicHeader第1个字节
  198.       *header = (0xc0 | c); 
  199. //ChunkBasicHeader大于1字节
  200. if (cSize) 
  201.         { 
  202. int tmp = packet->m_nChannel - 64; 
  203.           header[1] = tmp & 0xff; 
  204. if (cSize == 2) 
  205.         header[2] = tmp >> 8; 
  206.         } 
  207.     } 
  208.     } 
  209. if (tbuf) 
  210.     { 
  211. //
  212. int wrote = WriteN(r, tbuf, toff-tbuf); 
  213.       free(tbuf); 
  214.       tbuf = NULL; 
  215. if (!wrote) 
  216. return FALSE; 
  217.     } 
  218. /* we invoked a remote method */
  219. if (packet->m_packetType == 0x14) 
  220.     { 
  221.       AVal method; 
  222. char *ptr; 
  223.       ptr = packet->m_body + 1; 
  224.       AMF_DecodeString(ptr, &method); 
  225.       RTMP_Log(RTMP_LOGDEBUG, "Invoking %s", method.av_val); 
  226. /* keep it in call queue till result arrives */
  227. if (queue) { 
  228. int txn; 
  229.         ptr += 3 + method.av_len; 
  230.         txn = (int)AMF_DecodeNumber(ptr); 
  231.     AV_queue(&r->m_methodCalls, &r->m_numCalls, &method, txn); 
  232.       } 
  233.     } 
  234. if (!r->m_vecChannelsOut[packet->m_nChannel]) 
  235.     r->m_vecChannelsOut[packet->m_nChannel] = (RTMPPacket *) malloc(sizeof(RTMPPacket)); 
  236.   memcpy(r->m_vecChannelsOut[packet->m_nChannel], packet, sizeof(RTMPPacket)); 
  237. return TRUE; 

这个函数乍一看好像非常复杂,其实不然,他只是按照RTMP规范将数据编码成符合规范的块(Chunk),规范可以参考相关的文档。

具体怎么编码成块(Chunk)就不多分析了,在这里需要注意一个函数:WriteN()。该函数完成了将数据发送出去的功能。

来看一下WriteN()函数:

[cpp] view plaincopy

  1. //发送数据报的时候调用(连接,buffer,长度)
  2. static int
  3. WriteN(RTMP *r, const char *buffer, int n) 
  4. const char *ptr = buffer; 
  5. #ifdef CRYPTO
  6. char *encrypted = 0; 
  7. char buf[RTMP_BUFFER_CACHE_SIZE]; 
  8. if (r->Link.rc4keyOut) 
  9.     { 
  10. if (n > sizeof(buf)) 
  11.     encrypted = (char *)malloc(n); 
  12. else
  13.     encrypted = (char *)buf; 
  14.       ptr = encrypted; 
  15.       RC4_encrypt2((RC4_KEY *)r->Link.rc4keyOut, n, buffer, ptr); 
  16.     } 
  17. #endif
  18. while (n > 0) 
  19.     { 
  20. int nBytes; 
  21. //因方式的不同而调用不同函数
  22. //如果使用的是HTTP协议进行连接
  23. if (r->Link.protocol & RTMP_FEATURE_HTTP) 
  24.         nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n); 
  25. else
  26.         nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n); 
  27. /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
  28. //成功发送字节数<0
  29. if (nBytes < 0) 
  30.     { 
  31. int sockerr = GetSockError(); 
  32.       RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__, 
  33.           sockerr, n); 
  34. if (sockerr == EINTR && !RTMP_ctrlC) 
  35. continue; 
  36.       RTMP_Close(r); 
  37.       n = 1; 
  38. break; 
  39.     } 
  40. if (nBytes == 0) 
  41. break; 
  42.       n -= nBytes; 
  43.       ptr += nBytes; 
  44.     } 
  45. #ifdef CRYPTO
  46. if (encrypted && encrypted != buf) 
  47.     free(encrypted); 
  48. #endif
  49. return n == 0; 

该函数中,RTMPSockBuf_Send()完成了数据发送的功能,再来看看这个函数(函数调用真是好多啊。。。。)

[cpp] view plaincopy

  1. //Socket发送(指明套接字,buffer缓冲区,数据长度)
  2. //返回所发数据量
  3. int
  4. RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len) 
  5. int rc; 
  6. #ifdef _DEBUG
  7.   fwrite(buf, 1, len, netstackdump); 
  8. #endif
  9. #if defined(CRYPTO) && !defined(NO_SSL)
  10. if (sb->sb_ssl) 
  11.     { 
  12.       rc = TLS_write((SSL *)sb->sb_ssl, buf, len); 
  13.     } 
  14. else
  15. #endif
  16.     { 
  17. //向一个已连接的套接口发送数据。
  18. //int send( SOCKET s, const char * buf, int len, int flags);
  19. //s:一个用于标识已连接套接口的描述字。
  20. //buf:包含待发送数据的缓冲区。 
  21. //len:缓冲区中数据的长度。
  22. //flags:调用执行方式。
  23. //rc:所发数据量。
  24.       rc = send(sb->sb_socket, buf, len, 0); 
  25.     } 
  26. return rc; 
  27. int
  28. RTMPSockBuf_Close(RTMPSockBuf *sb) 
  29. #if defined(CRYPTO) && !defined(NO_SSL)
  30. if (sb->sb_ssl) 
  31.     { 
  32.       TLS_shutdown((SSL *)sb->sb_ssl); 
  33.       TLS_close((SSL *)sb->sb_ssl); 
  34.       sb->sb_ssl = NULL; 
  35.     } 
  36. #endif
  37. return closesocket(sb->sb_socket); 

到这个函数的时候,发现一层层的调用终于完成了,最后调用了系统Socket的send()函数完成了数据的发送功能。

之前贴过一张图总结这个过程,可能理解起来要方便一些:RTMPDump源代码分析 0: 主要函数调用分析

9: 接收消息(Message)(接收视音频数据)

在这里在研究研究接收消息(Message)的源代码,接收消息最典型的应用就是接收视音频数据了,因为视频和音频分别都属于RTMP协议规范中的一种消息。在这里主要分析接收视音频数据。

RTMPdump中完成视音频数据的接收(也可以说是视音频数据的下载)的函数是:RTMP_Read()。

RTMPdump主程序中的Download()函数就是通过调用RTMP_Read()完成数据接收,从而实现下载的。

那么我们马上开始吧,首先看看RTMP_Read()函数:

[cpp] view plaincopy

  1. //FLV文件头
  2. static const char flvHeader[] = { ‘F‘, ‘L‘, ‘V‘, 0x01, 
  3.   0x00,             /* 0x04代表有音频, 0x01代表有视频 */
  4.   0x00, 0x00, 0x00, 0x09, 
  5.   0x00, 0x00, 0x00, 0x00 
  6. }; 
  7. #define HEADERBUF   (128*1024)
  8. int
  9. RTMP_Read(RTMP *r, char *buf, int size) 
  10. int nRead = 0, total = 0; 
  11. /* can‘t continue */
  12. fail: 
  13. switch (r->m_read.status) { 
  14. case RTMP_READ_EOF: 
  15. case RTMP_READ_COMPLETE: 
  16. return 0; 
  17. case RTMP_READ_ERROR:  /* corrupted stream, resume failed */
  18.     SetSockError(EINVAL); 
  19. return -1; 
  20. default: 
  21. break; 
  22.   } 
  23. /* first time thru */
  24. if (!(r->m_read.flags & RTMP_READ_HEADER)) 
  25.     { 
  26. if (!(r->m_read.flags & RTMP_READ_RESUME)) 
  27.     { 
  28. //分配内存,指向buf的首部和尾部
  29. char *mybuf = (char *) malloc(HEADERBUF), *end = mybuf + HEADERBUF; 
  30. int cnt = 0; 
  31. //buf指向同一地址
  32.       r->m_read.buf = mybuf; 
  33.       r->m_read.buflen = HEADERBUF; 
  34. //把Flv的首部复制到mybuf指向的内存
  35. //RTMP传递的多媒体数据是“砍头”的FLV文件
  36.       memcpy(mybuf, flvHeader, sizeof(flvHeader)); 
  37. //m_read.buf指针后移flvheader个单位
  38.       r->m_read.buf += sizeof(flvHeader); 
  39. //buf长度增加flvheader长度
  40.       r->m_read.buflen -= sizeof(flvHeader); 
  41. //timestamp=0,不是多媒体数据
  42. while (r->m_read.timestamp == 0) 
  43.         { 
  44. //读取一个Packet,到r->m_read.buf
  45. //nRead为读取结果标记
  46.           nRead = Read_1_Packet(r, r->m_read.buf, r->m_read.buflen); 
  47. //有错误
  48. if (nRead < 0) 
  49.         { 
  50.           free(mybuf); 
  51.           r->m_read.buf = NULL; 
  52.           r->m_read.buflen = 0; 
  53.           r->m_read.status = nRead; 
  54. goto fail; 
  55.         } 
  56. /* buffer overflow, fix buffer and give up */
  57. if (r->m_read.buf < mybuf || r->m_read.buf > end) { 
  58.             mybuf = (char *) realloc(mybuf, cnt + nRead); 
  59.         memcpy(mybuf+cnt, r->m_read.buf, nRead); 
  60.         r->m_read.buf = mybuf+cnt+nRead; 
  61. break; 
  62.           } 
  63. //
  64. //记录读取的字节数
  65.           cnt += nRead; 
  66. //m_read.buf指针后移nRead个单位
  67.           r->m_read.buf += nRead; 
  68.           r->m_read.buflen -= nRead; 
  69. //当dataType=00000101时,即有视频和音频时
  70. //说明有多媒体数据了
  71. if (r->m_read.dataType == 5) 
  72. break; 
  73.         } 
  74. //读入数据类型
  75. //注意:mybuf指针位置一直没动
  76. //mybuf[4]中第 6 位表示是否存在音频Tag。第 8 位表示是否存在视频Tag。
  77.       mybuf[4] = r->m_read.dataType; 
  78. //两个指针之间的差
  79.       r->m_read.buflen = r->m_read.buf - mybuf; 
  80.       r->m_read.buf = mybuf; 
  81. //这句很重要!后面memcopy
  82.       r->m_read.bufpos = mybuf; 
  83.     } 
  84. //flags标明已经读完了文件头
  85.       r->m_read.flags |= RTMP_READ_HEADER; 
  86.     } 
  87. if ((r->m_read.flags & RTMP_READ_SEEKING) && r->m_read.buf) 
  88.     { 
  89. /* drop whatever‘s here */
  90.       free(r->m_read.buf); 
  91.       r->m_read.buf = NULL; 
  92.       r->m_read.bufpos = NULL; 
  93.       r->m_read.buflen = 0; 
  94.     } 
  95. /* If there‘s leftover data buffered, use it up */
  96. if (r->m_read.buf) 
  97.     { 
  98.       nRead = r->m_read.buflen; 
  99. if (nRead > size) 
  100.     nRead = size; 
  101. //m_read.bufpos指向mybuf
  102.       memcpy(buf, r->m_read.bufpos, nRead); 
  103.       r->m_read.buflen -= nRead; 
  104. if (!r->m_read.buflen) 
  105.     { 
  106.       free(r->m_read.buf); 
  107.       r->m_read.buf = NULL; 
  108.       r->m_read.bufpos = NULL; 
  109.     } 
  110. else
  111.     { 
  112.       r->m_read.bufpos += nRead; 
  113.     } 
  114.       buf += nRead; 
  115.       total += nRead; 
  116.       size -= nRead; 
  117.     } 
  118. //接着读
  119. while (size > 0 && (nRead = Read_1_Packet(r, buf, size)) >= 0) 
  120.     { 
  121. if (!nRead) continue; 
  122.       buf += nRead; 
  123.       total += nRead; 
  124.       size -= nRead; 
  125. break; 
  126.     } 
  127. if (nRead < 0) 
  128.     r->m_read.status = nRead; 
  129. if (size < 0) 
  130.     total += size; 
  131. return total; 

程序关键的地方都已经注释上了代码,在此就不重复说明了。有一点要提一下:RTMP传送的视音频数据的格式和FLV(FLash Video)格式是一样的,把接收下来的数据直接存入文件就可以了。但是这些视音频数据没有文件头,是纯视音频数据,因此需要在其前面加上FLV格式的文件头,这样得到的数据存成文件后才能被一般的视频播放器所播放。FLV格式的文件头是13个字节,如代码中所示。

RTMP_Read()中实际读取数据的函数是Read_1_Packet(),它的功能是从网络上读取一个RTMPPacket的数据,来看看它的源代码吧:

[cpp] view plaincopy

  1. /* 从流媒体中读取多媒体packet。
  2. * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
  3. * packets, 0 if ignorable error, >0 if there is a media packet
  4. */
  5. static int
  6. Read_1_Packet(RTMP *r, char *buf, unsigned int buflen) 
  7.   uint32_t prevTagSize = 0; 
  8. int rtnGetNextMediaPacket = 0, ret = RTMP_READ_EOF; 
  9.   RTMPPacket packet = { 0 }; 
  10. int recopy = FALSE; 
  11.   unsigned int size; 
  12. char *ptr, *pend; 
  13.   uint32_t nTimeStamp = 0; 
  14.   unsigned int len; 
  15. //获取下一个packet
  16.   rtnGetNextMediaPacket = RTMP_GetNextMediaPacket(r, &packet); 
  17. while (rtnGetNextMediaPacket) 
  18.     { 
  19. char *packetBody = packet.m_body; 
  20.       unsigned int nPacketLen = packet.m_nBodySize; 
  21. /* Return -3 if this was completed nicely with invoke message
  22.        * Play.Stop or Play.Complete
  23.        */
  24. if (rtnGetNextMediaPacket == 2) 
  25.     { 
  26.       RTMP_Log(RTMP_LOGDEBUG, 
  27. "Got Play.Complete or Play.Stop from server. "
  28. "Assuming stream is complete"); 
  29.       ret = RTMP_READ_COMPLETE; 
  30. break; 
  31.     } 
  32. //设置dataType
  33.       r->m_read.dataType |= (((packet.m_packetType == 0x08) << 2) | 
  34.                  (packet.m_packetType == 0x09)); 
  35. //MessageID为9时,为视频数据,数据太小时。。。
  36. if (packet.m_packetType == 0x09 && nPacketLen <= 5) 
  37.     { 
  38.       RTMP_Log(RTMP_LOGDEBUG, "ignoring too small video packet: size: %d", 
  39.           nPacketLen); 
  40.       ret = RTMP_READ_IGNORE; 
  41. break; 
  42.     } 
  43. //MessageID为8时,为音频数据,数据太小时。。。
  44. if (packet.m_packetType == 0x08 && nPacketLen <= 1) 
  45.     { 
  46.       RTMP_Log(RTMP_LOGDEBUG, "ignoring too small audio packet: size: %d", 
  47.           nPacketLen); 
  48.       ret = RTMP_READ_IGNORE; 
  49. break; 
  50.     } 
  51. if (r->m_read.flags & RTMP_READ_SEEKING) 
  52.     { 
  53.       ret = RTMP_READ_IGNORE; 
  54. break; 
  55.     } 
  56. #ifdef _DEBUG
  57.       RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d", 
  58.       packet.m_packetType, nPacketLen, packet.m_nTimeStamp, 
  59.       packet.m_hasAbsTimestamp); 
  60. if (packet.m_packetType == 0x09) 
  61.     RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0)); 
  62. #endif
  63. if (r->m_read.flags & RTMP_READ_RESUME) 
  64.     { 
  65. /* check the header if we get one */
  66. //此类packet的timestamp都是0
  67. if (packet.m_nTimeStamp == 0) 
  68.         { 
  69. //messageID=18,数据消息(AMF0)
  70. if (r->m_read.nMetaHeaderSize > 0 
  71.           && packet.m_packetType == 0x12) 
  72.         { 
  73. //获取metadata
  74.           AMFObject metaObj; 
  75. int nRes = 
  76.             AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE); 
  77. if (nRes >= 0) 
  78.             { 
  79.               AVal metastring; 
  80.               AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), 
  81.                     &metastring); 
  82. if (AVMATCH(&metastring, &av_onMetaData)) 
  83.             { 
  84. /* compare */
  85. if ((r->m_read.nMetaHeaderSize != nPacketLen) || 
  86.                   (memcmp 
  87.                    (r->m_read.metaHeader, packetBody, 
  88.                 r->m_read.nMetaHeaderSize) != 0)) 
  89.                 { 
  90.                   ret = RTMP_READ_ERROR; 
  91.                 } 
  92.             } 
  93.               AMF_Reset(&metaObj); 
  94. if (ret == RTMP_READ_ERROR) 
  95. break; 
  96.             } 
  97.         } 
  98. /* check first keyframe to make sure we got the right position
  99.            * in the stream! (the first non ignored frame)
  100.            */
  101. if (r->m_read.nInitialFrameSize > 0) 
  102.         { 
  103. /* video or audio data */
  104. if (packet.m_packetType == r->m_read.initialFrameType 
  105.               && r->m_read.nInitialFrameSize == nPacketLen) 
  106.             { 
  107. /* we don‘t compare the sizes since the packet can
  108.                * contain several FLV packets, just make sure the
  109.                * first frame is our keyframe (which we are going
  110.                * to rewrite)
  111.                */
  112. if (memcmp 
  113.               (r->m_read.initialFrame, packetBody, 
  114.                r->m_read.nInitialFrameSize) == 0) 
  115.             { 
  116.               RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!"); 
  117.               r->m_read.flags |= RTMP_READ_GOTKF; 
  118. /* ignore it! (what about audio data after it? it is
  119.                * handled by ignoring all 0ms frames, see below)
  120.                */
  121.               ret = RTMP_READ_IGNORE; 
  122. break; 
  123.             } 
  124.             } 
  125. /* hande FLV streams, even though the server resends the
  126.            * keyframe as an extra video packet it is also included
  127.            * in the first FLV stream chunk and we have to compare
  128.            * it and filter it out !!
  129.            */
  130. //MessageID=22,聚合消息
  131. if (packet.m_packetType == 0x16) 
  132.             { 
  133. /* basically we have to find the keyframe with the
  134.                * correct TS being nResumeTS
  135.                */
  136.               unsigned int pos = 0; 
  137.               uint32_t ts = 0; 
  138. while (pos + 11 < nPacketLen) 
  139.             { 
  140. /* size without header (11) and prevTagSize (4) */
  141.               uint32_t dataSize = 
  142.                 AMF_DecodeInt24(packetBody + pos + 1); 
  143.               ts = AMF_DecodeInt24(packetBody + pos + 4); 
  144.               ts |= (packetBody[pos + 7] << 24); 
  145. #ifdef _DEBUG
  146.               RTMP_Log(RTMP_LOGDEBUG, 
  147. "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms", 
  148.                   packetBody[pos], dataSize, ts); 
  149. #endif
  150. /* ok, is it a keyframe?:
  151.                * well doesn‘t work for audio!
  152.                */
  153. if (packetBody[pos /*6928, test 0 */ ] == 
  154.                   r->m_read.initialFrameType 
  155. /* && (packetBody[11]&0xf0) == 0x10 */ ) 
  156.                 { 
  157. if (ts == r->m_read.nResumeTS) 
  158.                 { 
  159.                   RTMP_Log(RTMP_LOGDEBUG, 
  160. "Found keyframe with resume-keyframe timestamp!"); 
  161. if (r->m_read.nInitialFrameSize != dataSize 
  162.                       || memcmp(r->m_read.initialFrame, 
  163.                         packetBody + pos + 11, 
  164.                         r->m_read. 
  165.                         nInitialFrameSize) != 0) 
  166.                     { 
  167.                       RTMP_Log(RTMP_LOGERROR, 
  168. "FLV Stream: Keyframe doesn‘t match!"); 
  169.                       ret = RTMP_READ_ERROR; 
  170. break; 
  171.                     } 
  172.                   r->m_read.flags |= RTMP_READ_GOTFLVK; 
  173. /* skip this packet?
  174.                    * check whether skippable:
  175.                    */
  176. if (pos + 11 + dataSize + 4 > nPacketLen) 
  177.                     { 
  178.                       RTMP_Log(RTMP_LOGWARNING, 
  179. "Non skipable packet since it doesn‘t end with chunk, stream corrupt!"); 
  180.                       ret = RTMP_READ_ERROR; 
  181. break; 
  182.                     } 
  183.                   packetBody += (pos + 11 + dataSize + 4); 
  184.                   nPacketLen -= (pos + 11 + dataSize + 4); 
  185. goto stopKeyframeSearch; 
  186.                 } 
  187. else if (r->m_read.nResumeTS < ts) 
  188.                 { 
  189. /* the timestamp ts will only increase with
  190.                    * further packets, wait for seek
  191.                    */
  192. goto stopKeyframeSearch; 
  193.                 } 
  194.                 } 
  195.               pos += (11 + dataSize + 4); 
  196.             } 
  197. if (ts < r->m_read.nResumeTS) 
  198.             { 
  199.               RTMP_Log(RTMP_LOGERROR, 
  200. "First packet does not contain keyframe, all "
  201. "timestamps are smaller than the keyframe "
  202. "timestamp; probably the resume seek failed?"); 
  203.             } 
  204.             stopKeyframeSearch: 
  205.               ; 
  206. if (!(r->m_read.flags & RTMP_READ_GOTFLVK)) 
  207.             { 
  208.               RTMP_Log(RTMP_LOGERROR, 
  209. "Couldn‘t find the seeked keyframe in this chunk!"); 
  210.               ret = RTMP_READ_IGNORE; 
  211. break; 
  212.             } 
  213.             } 
  214.         } 
  215.         } 
  216. if (packet.m_nTimeStamp > 0 
  217.           && (r->m_read.flags & (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK))) 
  218.         { 
  219. /* another problem is that the server can actually change from
  220.            * 09/08 video/audio packets to an FLV stream or vice versa and
  221.            * our keyframe check will prevent us from going along with the
  222.            * new stream if we resumed.
  223.            *
  224.            * in this case set the ‘found keyframe‘ variables to true.
  225.            * We assume that if we found one keyframe somewhere and were
  226.            * already beyond TS > 0 we have written data to the output
  227.            * which means we can accept all forthcoming data including the
  228.            * change between 08/09 <-> FLV packets
  229.            */
  230.           r->m_read.flags |= (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK); 
  231.         } 
  232. /* skip till we find our keyframe
  233.        * (seeking might put us somewhere before it)
  234.        */
  235. if (!(r->m_read.flags & RTMP_READ_GOTKF) && 
  236.         packet.m_packetType != 0x16) 
  237.         { 
  238.           RTMP_Log(RTMP_LOGWARNING, 
  239. "Stream does not start with requested frame, ignoring data... "); 
  240.           r->m_read.nIgnoredFrameCounter++; 
  241. if (r->m_read.nIgnoredFrameCounter > MAX_IGNORED_FRAMES) 
  242.         ret = RTMP_READ_ERROR;  /* fatal error, couldn‘t continue stream */
  243. else
  244.         ret = RTMP_READ_IGNORE; 
  245. break; 
  246.         } 
  247. /* ok, do the same for FLV streams */
  248. if (!(r->m_read.flags & RTMP_READ_GOTFLVK) && 
  249.         packet.m_packetType == 0x16) 
  250.         { 
  251.           RTMP_Log(RTMP_LOGWARNING, 
  252. "Stream does not start with requested FLV frame, ignoring data... "); 
  253.           r->m_read.nIgnoredFlvFrameCounter++; 
  254. if (r->m_read.nIgnoredFlvFrameCounter > MAX_IGNORED_FRAMES) 
  255.         ret = RTMP_READ_ERROR; 
  256. else
  257.         ret = RTMP_READ_IGNORE; 
  258. break; 
  259.         } 
  260. /* we have to ignore the 0ms frames since these are the first
  261.        * keyframes; we‘ve got these so don‘t mess around with multiple
  262.        * copies sent by the server to us! (if the keyframe is found at a
  263.        * later position there is only one copy and it will be ignored by
  264.        * the preceding if clause)
  265.        */
  266. if (!(r->m_read.flags & RTMP_READ_NO_IGNORE) && 
  267.         packet.m_packetType != 0x16) 
  268.         {           /* exclude type 0x16 (FLV) since it can
  269.                  * contain several FLV packets */
  270. if (packet.m_nTimeStamp == 0) 
  271.         { 
  272.           ret = RTMP_READ_IGNORE; 
  273. break; 
  274.         } 
  275. else
  276.         { 
  277. /* stop ignoring packets */
  278.           r->m_read.flags |= RTMP_READ_NO_IGNORE; 
  279.         } 
  280.         } 
  281.     } 
  282. /* calculate packet size and allocate slop buffer if necessary */
  283.       size = nPacketLen + 
  284.     ((packet.m_packetType == 0x08 || packet.m_packetType == 0x09 
  285.       || packet.m_packetType == 0x12) ? 11 : 0) + 
  286.     (packet.m_packetType != 0x16 ? 4 : 0); 
  287. if (size + 4 > buflen) 
  288.     { 
  289. /* the extra 4 is for the case of an FLV stream without a last
  290.        * prevTagSize (we need extra 4 bytes to append it) */
  291.       r->m_read.buf = (char *) malloc(size + 4); 
  292. if (r->m_read.buf == 0) 
  293.         { 
  294.           RTMP_Log(RTMP_LOGERROR, "Couldn‘t allocate memory!"); 
  295.           ret = RTMP_READ_ERROR;        /* fatal error */
  296. break; 
  297.         } 
  298.       recopy = TRUE; 
  299.       ptr = r->m_read.buf; 
  300.     } 
  301. else
  302.     { 
  303.       ptr = buf; 
  304.     } 
  305.       pend = ptr + size + 4; 
  306. /* use to return timestamp of last processed packet */
  307. /* audio (0x08), video (0x09) or metadata (0x12) packets :
  308.        * construct 11 byte header then add rtmp packet‘s data */
  309. if (packet.m_packetType == 0x08 || packet.m_packetType == 0x09 
  310.       || packet.m_packetType == 0x12) 
  311.     { 
  312.       nTimeStamp = r->m_read.nResumeTS + packet.m_nTimeStamp; 
  313.       prevTagSize = 11 + nPacketLen; 
  314.       *ptr = packet.m_packetType; 
  315.       ptr++; 
  316.       ptr = AMF_EncodeInt24(ptr, pend, nPacketLen); 
  317. #if 0
  318. if(packet.m_packetType == 0x09) { /* video */
  319. /* H264 fix: */
  320. if((packetBody[0] & 0x0f) == 7) { /* CodecId = H264 */
  321.          uint8_t packetType = *(packetBody+1); 
  322.          uint32_t ts = AMF_DecodeInt24(packetBody+2); /* composition time */
  323.          int32_t cts = (ts+0xff800000)^0xff800000; 
  324.          RTMP_Log(RTMP_LOGDEBUG, "cts  : %d\n", cts); 
  325.          nTimeStamp -= cts; 
  326. /* get rid of the composition time */
  327.          CRTMP::EncodeInt24(packetBody+2, 0); 
  328.          } 
  329.          RTMP_Log(RTMP_LOGDEBUG, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp, nTimeStamp); 
  330.          } 
  331. #endif
  332.       ptr = AMF_EncodeInt24(ptr, pend, nTimeStamp); 
  333.       *ptr = (char)((nTimeStamp & 0xFF000000) >> 24); 
  334.       ptr++; 
  335. /* stream id */
  336.       ptr = AMF_EncodeInt24(ptr, pend, 0); 
  337.     } 
  338.       memcpy(ptr, packetBody, nPacketLen); 
  339.       len = nPacketLen; 
  340. /* correct tagSize and obtain timestamp if we have an FLV stream */
  341. if (packet.m_packetType == 0x16) 
  342.     { 
  343.       unsigned int pos = 0; 
  344. int delta; 
  345. /* grab first timestamp and see if it needs fixing */
  346. //    nTimeStamp = AMF_DecodeInt24(packetBody + 4);
  347. //  nTimeStamp |= (packetBody[7] << 24);
  348. //    delta = packet.m_nTimeStamp - nTimeStamp;
  349. while (pos + 11 < nPacketLen) 
  350.         { 
  351. /* size without header (11) and without prevTagSize (4) */
  352.           uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1); 
  353.           nTimeStamp = AMF_DecodeInt24(packetBody + pos + 4); 
  354.           nTimeStamp |= (packetBody[pos + 7] << 24); 
  355. //        if (delta)
  356. //      {
  357. //        nTimeStamp += delta;
  358. //        AMF_EncodeInt24(ptr+pos+4, pend, nTimeStamp);
  359. //        ptr[pos+7] = nTimeStamp>>24;
  360. //      }
  361. /* set data type */
  362.           r->m_read.dataType |= (((*(packetBody + pos) == 0x08) << 2) | 
  363.                      (*(packetBody + pos) == 0x09)); 
  364. if (pos + 11 + dataSize + 4 > nPacketLen) 
  365.         { 
  366. if (pos + 11 + dataSize > nPacketLen) 
  367.             { 
  368.               RTMP_Log(RTMP_LOGERROR, 
  369. "Wrong data size (%lu), stream corrupted, aborting!", 
  370.               dataSize); 
  371.               ret = RTMP_READ_ERROR; 
  372. break; 
  373.             } 
  374.           RTMP_Log(RTMP_LOGWARNING, "No tagSize found, appending!"); 
  375. /* we have to append a last tagSize! */
  376.           prevTagSize = dataSize + 11; 
  377.           AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend, 
  378.                   prevTagSize); 
  379.           size += 4; 
  380.           len += 4; 
  381.         } 
  382. else
  383.         { 
  384.           prevTagSize = 
  385.             AMF_DecodeInt32(packetBody + pos + 11 + dataSize); 
  386. #ifdef _DEBUG
  387.           RTMP_Log(RTMP_LOGDEBUG, 
  388. "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms", 
  389.               (unsigned char)packetBody[pos], dataSize, prevTagSize, 
  390.               nTimeStamp); 
  391. #endif
  392. if (prevTagSize != (dataSize + 11)) 
  393.             { 
  394. #ifdef _DEBUG
  395.               RTMP_Log(RTMP_LOGWARNING, 
  396. "Tag and data size are not consitent, writing tag size according to dataSize+11: %d", 
  397.               dataSize + 11); 
  398. #endif
  399.               prevTagSize = dataSize + 11; 
  400.               AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend, 
  401.                       prevTagSize); 
  402.             } 
  403.         } 
  404.           pos += prevTagSize + 4;   /*(11+dataSize+4); */
  405.         } 
  406.     } 
  407.       ptr += len; 
  408. if (packet.m_packetType != 0x16) 
  409.     { 
  410. /* FLV tag packets contain their own prevTagSize */
  411.       AMF_EncodeInt32(ptr, pend, prevTagSize); 
  412.     } 
  413. /* In non-live this nTimeStamp can contain an absolute TS.
  414.        * Update ext timestamp with this absolute offset in non-live mode
  415.        * otherwise report the relative one
  416.        */
  417. /* RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, pktTS: %dms, TS: %dms, bLiveStream: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, nTimeStamp, r->Link.lFlags & RTMP_LF_LIVE); */
  418.       r->m_read.timestamp = (r->Link.lFlags & RTMP_LF_LIVE) ? packet.m_nTimeStamp : nTimeStamp; 
  419.       ret = size; 
  420. break; 
  421.     } 
  422. if (rtnGetNextMediaPacket) 
  423.     RTMPPacket_Free(&packet); 
  424. if (recopy) 
  425.     { 
  426.       len = ret > buflen ? buflen : ret; 
  427.       memcpy(buf, r->m_read.buf, len); 
  428.       r->m_read.bufpos = r->m_read.buf + len; 
  429.       r->m_read.buflen = ret - len; 
  430.     } 
  431. return ret; 

函数功能很多,重要的地方已经加上了注释,在此不再细分析。Read_1_Packet()里面实现从网络中读取视音频数据的函数是RTMP_GetNextMediaPacket()。下面我们来看看该函数的源代码:

[cpp] view plaincopy

  1. int
  2. RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet) 
  3. int bHasMediaPacket = 0; 
  4. while (!bHasMediaPacket && RTMP_IsConnected(r) 
  5.      && RTMP_ReadPacket(r, packet)) 
  6.     { 
  7. if (!RTMPPacket_IsReady(packet)) 
  8.     { 
  9. continue; 
  10.     } 
  11.       bHasMediaPacket = RTMP_ClientPacket(r, packet); 
  12. if (!bHasMediaPacket) 
  13.     { 
  14.       RTMPPacket_Free(packet); 
  15.     } 
  16. else if (r->m_pausing == 3) 
  17.     { 
  18. if (packet->m_nTimeStamp <= r->m_mediaStamp) 
  19.         { 
  20.           bHasMediaPacket = 0; 
  21. #ifdef _DEBUG
  22.           RTMP_Log(RTMP_LOGDEBUG, 
  23. "Skipped type: %02X, size: %d, TS: %d ms, abs TS: %d, pause: %d ms", 
  24.           packet->m_packetType, packet->m_nBodySize, 
  25.           packet->m_nTimeStamp, packet->m_hasAbsTimestamp, 
  26.           r->m_mediaStamp); 
  27. #endif
  28. continue; 
  29.         } 
  30.       r->m_pausing = 0; 
  31.     } 
  32.     } 
  33. if (bHasMediaPacket) 
  34.     r->m_bPlaying = TRUE; 
  35. else if (r->m_sb.sb_timedout && !r->m_pausing) 
  36.     r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel]; 
  37. return bHasMediaPacket; 

这里有两个函数比较重要:RTMP_ReadPacket()以及RTMP_ClientPacket()。这两个函数中,前一个函数负责从网络上读取数据,后一个负责处理数据。这部分与建立RTMP连接的网络流(NetStream)的时候很相似,参考:RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)

RTMP_ClientPacket()在前文中已经做过分析,在此不再重复叙述。在这里重点分析一下RTMP_ReadPacket(),来看看它的源代码。

[cpp] view plaincopy

  1. //读取收下来的Chunk
  2. int
  3. RTMP_ReadPacket(RTMP *r, RTMPPacket *packet) 
  4. //packet 存读取完后的的数据
  5. //Chunk Header最大值18
  6.   uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 }; 
  7. //header 指向的是从Socket中收下来的数据
  8. char *header = (char *)hbuf; 
  9. int nSize, hSize, nToRead, nChunk; 
  10. int didAlloc = FALSE; 
  11.   RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket); 
  12. //收下来的数据存入hbuf
  13. if (ReadN(r, (char *)hbuf, 1) == 0) 
  14.     { 
  15.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__); 
  16. return FALSE; 
  17.     } 
  18. //块类型fmt
  19.   packet->m_headerType = (hbuf[0] & 0xc0) >> 6; 
  20. //块流ID(2-63)
  21.   packet->m_nChannel = (hbuf[0] & 0x3f); 
  22.   header++; 
  23. //块流ID第1字节为0时,块流ID占2个字节
  24. if (packet->m_nChannel == 0) 
  25.     { 
  26. if (ReadN(r, (char *)&hbuf[1], 1) != 1) 
  27.     { 
  28.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 2nd byte", 
  29.           __FUNCTION__); 
  30. return FALSE; 
  31.     } 
  32. //计算块流ID(64-319)
  33.       packet->m_nChannel = hbuf[1]; 
  34.       packet->m_nChannel += 64; 
  35.       header++; 
  36.     } 
  37. //块流ID第1字节为0时,块流ID占3个字节
  38. else if (packet->m_nChannel == 1) 
  39.     { 
  40. int tmp; 
  41. if (ReadN(r, (char *)&hbuf[1], 2) != 2) 
  42.     { 
  43.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 3nd byte", 
  44.           __FUNCTION__); 
  45. return FALSE; 
  46.     } 
  47.       tmp = (hbuf[2] << 8) + hbuf[1]; 
  48. //计算块流ID(64-65599)
  49.       packet->m_nChannel = tmp + 64; 
  50.       RTMP_Log(RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel); 
  51.       header += 2; 
  52.     } 
  53. //ChunkHeader的大小(4种)
  54.   nSize = packetSize[packet->m_headerType]; 
  55. if (nSize == RTMP_LARGE_HEADER_SIZE)  /* if we get a full header the timestamp is absolute */
  56.     packet->m_hasAbsTimestamp = TRUE;    //11字节的完整ChunkMsgHeader的TimeStamp是绝对值
  57. else if (nSize < RTMP_LARGE_HEADER_SIZE) 
  58.     {               /* using values from the last message of this channel */
  59. if (r->m_vecChannelsIn[packet->m_nChannel]) 
  60.     memcpy(packet, r->m_vecChannelsIn[packet->m_nChannel], 
  61. sizeof(RTMPPacket)); 
  62.     } 
  63.   nSize--; 
  64. if (nSize > 0 && ReadN(r, header, nSize) != nSize) 
  65.     { 
  66.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header. type: %x", 
  67.       __FUNCTION__, (unsigned int)hbuf[0]); 
  68. return FALSE; 
  69.     } 
  70.   hSize = nSize + (header - (char *)hbuf); 
  71. if (nSize >= 3) 
  72.     { 
  73. //TimeStamp(注意 BigEndian to SmallEndian)(11,7,3字节首部都有)
  74.       packet->m_nTimeStamp = AMF_DecodeInt24(header); 
  75. /*RTMP_Log(RTMP_LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nTimeStamp, packet.m_hasAbsTimestamp); */
  76. //消息长度(11,7字节首部都有)
  77. if (nSize >= 6) 
  78.     { 
  79.       packet->m_nBodySize = AMF_DecodeInt24(header + 3); 
  80.       packet->m_nBytesRead = 0; 
  81.       RTMPPacket_Free(packet); 
  82. //(11,7字节首部都有)
  83. if (nSize > 6) 
  84.         { 
  85. //Msg type ID
  86.           packet->m_packetType = header[6]; 
  87. //Msg Stream ID
  88. if (nSize == 11) 
  89.         packet->m_nInfoField2 = DecodeInt32LE(header + 7); 
  90.         } 
  91.     } 
  92. //Extend TimeStamp
  93. if (packet->m_nTimeStamp == 0xffffff) 
  94.     { 
  95. if (ReadN(r, header + nSize, 4) != 4) 
  96.         { 
  97.           RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", 
  98.           __FUNCTION__); 
  99. return FALSE; 
  100.         } 
  101.       packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize); 
  102.       hSize += 4; 
  103.     } 
  104.     } 
  105.   RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize); 
  106. if (packet->m_nBodySize > 0 && packet->m_body == NULL) 
  107.     { 
  108. if (!RTMPPacket_Alloc(packet, packet->m_nBodySize)) 
  109.     { 
  110.       RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__); 
  111. return FALSE; 
  112.     } 
  113.       didAlloc = TRUE; 
  114.       packet->m_headerType = (hbuf[0] & 0xc0) >> 6; 
  115.     } 
  116.   nToRead = packet->m_nBodySize - packet->m_nBytesRead; 
  117.   nChunk = r->m_inChunkSize; 
  118. if (nToRead < nChunk) 
  119.     nChunk = nToRead; 
  120. /* Does the caller want the raw chunk? */
  121. if (packet->m_chunk) 
  122.     { 
  123.       packet->m_chunk->c_headerSize = hSize; 
  124.       memcpy(packet->m_chunk->c_header, hbuf, hSize); 
  125.       packet->m_chunk->c_chunk = packet->m_body + packet->m_nBytesRead; 
  126.       packet->m_chunk->c_chunkSize = nChunk; 
  127.     } 
  128. if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk) 
  129.     { 
  130.       RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %lu", 
  131.       __FUNCTION__, packet->m_nBodySize); 
  132. return FALSE; 
  133.     } 
  134.   RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)packet->m_body + packet->m_nBytesRead, nChunk); 
  135.   packet->m_nBytesRead += nChunk; 
  136. /* keep the packet as ref for other packets on this channel */
  137. if (!r->m_vecChannelsIn[packet->m_nChannel]) 
  138.     r->m_vecChannelsIn[packet->m_nChannel] = (RTMPPacket *) malloc(sizeof(RTMPPacket)); 
  139.   memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket)); 
  140. //读取完毕
  141. if (RTMPPacket_IsReady(packet)) 
  142.     { 
  143. /* make packet‘s timestamp absolute */
  144. if (!packet->m_hasAbsTimestamp) 
  145.     packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel]; /* timestamps seem to be always relative!! */
  146.       r->m_channelTimestamp[packet->m_nChannel] = packet->m_nTimeStamp; 
  147. /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
  148. /* arrives and requests to re-use some info (small packet header) */
  149.       r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL; 
  150.       r->m_vecChannelsIn[packet->m_nChannel]->m_nBytesRead = 0; 
  151.       r->m_vecChannelsIn[packet->m_nChannel]->m_hasAbsTimestamp = FALSE;   /* can only be false if we reuse header */
  152.     } 
  153. else
  154.     { 
  155.       packet->m_body = NULL; /* so it won‘t be erased on free */
  156.     } 
  157. return TRUE; 

函数代码看似很多,但是并不是很复杂,可以理解为在从事“简单重复性劳动”(和搬砖差不多)。基本上是一个字节一个字节的读取,然后按照RTMP协议规范进行解析。具体如何解析可以参考RTMP协议规范。

在RTMP_ReadPacket()函数里完成从Socket中读取数据的函数是ReadN(),继续看看它的源代码:

[cpp] view plaincopy

  1. //从HTTP或SOCKET中读取数据
  2. static int
  3. ReadN(RTMP *r, char *buffer, int n) 
  4. int nOriginalSize = n; 
  5. int avail; 
  6. char *ptr; 
  7.   r->m_sb.sb_timedout = FALSE; 
  8. #ifdef _DEBUG
  9.   memset(buffer, 0, n); 
  10. #endif
  11.   ptr = buffer; 
  12. while (n > 0) 
  13.     { 
  14. int nBytes = 0, nRead; 
  15. if (r->Link.protocol & RTMP_FEATURE_HTTP) 
  16.         { 
  17. while (!r->m_resplen) 
  18.         { 
  19. if (r->m_sb.sb_size < 144) 
  20.             { 
  21. if (!r->m_unackd) 
  22.             HTTP_Post(r, RTMPT_IDLE, "", 1); 
  23. if (RTMPSockBuf_Fill(&r->m_sb) < 1) 
  24.             { 
  25. if (!r->m_sb.sb_timedout) 
  26.                 RTMP_Close(r); 
  27. return 0; 
  28.             } 
  29.         } 
  30.           HTTP_read(r, 0); 
  31.         } 
  32. if (r->m_resplen && !r->m_sb.sb_size) 
  33.         RTMPSockBuf_Fill(&r->m_sb); 
  34.           avail = r->m_sb.sb_size; 
  35. if (avail > r->m_resplen) 
  36.         avail = r->m_resplen; 
  37.     } 
  38. else
  39.         { 
  40.           avail = r->m_sb.sb_size; 
  41. if (avail == 0) 
  42.         { 
  43. if (RTMPSockBuf_Fill(&r->m_sb) < 1) 
  44.             { 
  45. if (!r->m_sb.sb_timedout) 
  46.                 RTMP_Close(r); 
  47. return 0; 
  48.         } 
  49.           avail = r->m_sb.sb_size; 
  50.         } 
  51.     } 
  52.       nRead = ((n < avail) ? n : avail); 
  53. if (nRead > 0) 
  54.     { 
  55.       memcpy(ptr, r->m_sb.sb_start, nRead); 
  56.       r->m_sb.sb_start += nRead; 
  57.       r->m_sb.sb_size -= nRead; 
  58.       nBytes = nRead; 
  59.       r->m_nBytesIn += nRead; 
  60. if (r->m_bSendCounter 
  61.           && r->m_nBytesIn > r->m_nBytesInSent + r->m_nClientBW / 2) 
  62.         SendBytesReceived(r); 
  63.     } 
  64. /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
  65. #ifdef _DEBUG
  66.       fwrite(ptr, 1, nBytes, netstackdump_read); 
  67. #endif
  68. if (nBytes == 0) 
  69.     { 
  70.       RTMP_Log(RTMP_LOGDEBUG, "%s, RTMP socket closed by peer", __FUNCTION__); 
  71. /*goto again; */
  72.       RTMP_Close(r); 
  73. break; 
  74.     } 
  75. if (r->Link.protocol & RTMP_FEATURE_HTTP) 
  76.     r->m_resplen -= nBytes; 
  77. #ifdef CRYPTO
  78. if (r->Link.rc4keyIn) 
  79.     { 
  80.       RC4_encrypt((RC4_KEY *)r->Link.rc4keyIn, nBytes, ptr); 
  81.     } 
  82. #endif
  83.       n -= nBytes; 
  84.       ptr += nBytes; 
  85.     } 
  86. return nOriginalSize - n; 

ReadN()中实现从Socket中接收数据的函数是RTMPSockBuf_Fill(),看看代码吧(又是层层调用)。

[cpp] view plaincopy

  1. //调用Socket编程中的recv()函数,接收数据
  2. int
  3. RTMPSockBuf_Fill(RTMPSockBuf *sb) 
  4. int nBytes; 
  5. if (!sb->sb_size) 
  6.     sb->sb_start = sb->sb_buf; 
  7. while (1) 
  8.     { 
  9. //缓冲区长度:总长-未处理字节-已处理字节
  10. //|-----已处理--------|-----未处理--------|---------缓冲区----------|
  11. //sb_buf        sb_start    sb_size    
  12.       nBytes = sizeof(sb->sb_buf) - sb->sb_size - (sb->sb_start - sb->sb_buf); 
  13. #if defined(CRYPTO) && !defined(NO_SSL)
  14. if (sb->sb_ssl) 
  15.     { 
  16.       nBytes = TLS_read((SSL *)sb->sb_ssl, sb->sb_start + sb->sb_size, nBytes); 
  17.     } 
  18. else
  19. #endif
  20.     { 
  21. //int recv( SOCKET s, char * buf, int len, int flags);
  22. //s:一个标识已连接套接口的描述字。
  23. //buf:用于接收数据的缓冲区。
  24. //len:缓冲区长度。
  25. //flags:指定调用方式。
  26. //从sb_start(待处理的下一字节) + sb_size()还未处理的字节开始buffer为空,可以存储
  27.         nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0); 
  28.     } 
  29. if (nBytes != -1) 
  30.     { 
  31. //未处理的字节又多了
  32.       sb->sb_size += nBytes; 
  33.     } 
  34. else
  35.     { 
  36. int sockerr = GetSockError(); 
  37.       RTMP_Log(RTMP_LOGDEBUG, "%s, recv returned %d. GetSockError(): %d (%s)", 
  38.           __FUNCTION__, nBytes, sockerr, strerror(sockerr)); 
  39. if (sockerr == EINTR && !RTMP_ctrlC) 
  40. continue; 
  41. if (sockerr == EWOULDBLOCK || sockerr == EAGAIN) 
  42.         { 
  43.           sb->sb_timedout = TRUE; 
  44.           nBytes = 0; 
  45.         } 
  46.     } 
  47. break; 
  48.     } 
  49. return nBytes; 

从RTMPSockBuf_Fill()代码中可以看出,调用了系统Socket的recv()函数接收RTMP连接传输过来的数据。

10: 处理各种消息(Message)

已经连续写了一系列的博客了,其实大部分内容都是去年搞RTMP研究的时候积累的经验,回顾一下过去的知识,其实 RTMPdump(libRTMP)主要的功能也都分析的差不多了,现在感觉还需要一些查漏补缺。主要就是它是如何处理各种消息(Message)的这方面还没有研究的特明白,在此需要详细研究一下。

再来看一下RTMPdump(libRTMP)的“灵魂”函数RTMP_ClientPacket(),主要完成了各种消息的处理。

[cpp] view plaincopy

  1. //处理接收到的数据
  2. int
  3. RTMP_ClientPacket(RTMP *r, RTMPPacket *packet) 
  4. int bHasMediaPacket = 0; 
  5. switch (packet->m_packetType) 
  6.     { 
  7. //RTMP消息类型ID=1,设置块大小
  8. case 0x01: 
  9. /* chunk size */
  10. //----------------
  11.         r->dlg->AppendCInfo("处理收到的数据。消息 Set Chunk Size (typeID=1)。"); 
  12. //-----------------------------
  13.         RTMP_LogPrintf("处理消息 Set Chunk Size (typeID=1)\n"); 
  14.       HandleChangeChunkSize(r, packet); 
  15. break; 
  16. //RTMP消息类型ID=3,致谢
  17. case 0x03: 
  18. /* bytes read report */
  19.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); 
  20. break; 
  21. //RTMP消息类型ID=4,用户控制
  22. case 0x04: 
  23. /* ctrl */
  24. //----------------
  25.         r->dlg->AppendCInfo("处理收到的数据。消息 User Control (typeID=4)。"); 
  26. //-----------------------------
  27.         RTMP_LogPrintf("处理消息 User Control (typeID=4)\n"); 
  28.       HandleCtrl(r, packet); 
  29. break; 
  30. //RTMP消息类型ID=5
  31. case 0x05: 
  32. /* server bw */
  33. //----------------
  34.         r->dlg->AppendCInfo("处理收到的数据。消息 Window Acknowledgement Size (typeID=5)。"); 
  35. //-----------------------------
  36.         RTMP_LogPrintf("处理消息 Window Acknowledgement Size (typeID=5)\n"); 
  37.       HandleServerBW(r, packet); 
  38. break; 
  39. //RTMP消息类型ID=6
  40. case 0x06: 
  41. /* client bw */
  42. //----------------
  43.         r->dlg->AppendCInfo("处理收到的数据。消息 Set Peer Bandwidth (typeID=6)。"); 
  44. //-----------------------------
  45.         RTMP_LogPrintf("处理消息 Set Peer Bandwidth (typeID=6)\n"); 
  46.       HandleClientBW(r, packet); 
  47. break; 
  48. //RTMP消息类型ID=8,音频数据
  49. case 0x08: 
  50. /* audio data */
  51. /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
  52.       HandleAudio(r, packet); 
  53.       bHasMediaPacket = 1; 
  54. if (!r->m_mediaChannel) 
  55.     r->m_mediaChannel = packet->m_nChannel; 
  56. if (!r->m_pausing) 
  57.     r->m_mediaStamp = packet->m_nTimeStamp; 
  58. break; 
  59. //RTMP消息类型ID=9,视频数据
  60. case 0x09: 
  61. /* video data */
  62. /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
  63.       HandleVideo(r, packet); 
  64.       bHasMediaPacket = 1; 
  65. if (!r->m_mediaChannel) 
  66.     r->m_mediaChannel = packet->m_nChannel; 
  67. if (!r->m_pausing) 
  68.     r->m_mediaStamp = packet->m_nTimeStamp; 
  69. break; 
  70. //RTMP消息类型ID=15,AMF3编码,忽略
  71. case 0x0F:          /* flex stream send */
  72.       RTMP_Log(RTMP_LOGDEBUG, 
  73. "%s, flex stream send, size %lu bytes, not supported, ignoring", 
  74.       __FUNCTION__, packet->m_nBodySize); 
  75. break; 
  76. //RTMP消息类型ID=16,AMF3编码,忽略
  77. case 0x10:          /* flex shared object */
  78.       RTMP_Log(RTMP_LOGDEBUG, 
  79. "%s, flex shared object, size %lu bytes, not supported, ignoring", 
  80.       __FUNCTION__, packet->m_nBodySize); 
  81. break; 
  82. //RTMP消息类型ID=17,AMF3编码,忽略
  83. case 0x11:          /* flex message */
  84.       { 
  85.     RTMP_Log(RTMP_LOGDEBUG, 
  86. "%s, flex message, size %lu bytes, not fully supported", 
  87.         __FUNCTION__, packet->m_nBodySize); 
  88. /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  89. /* some DEBUG code */
  90. #if 0
  91.        RTMP_LIB_AMFObject obj; 
  92. int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1); 
  93. if(nRes < 0) { 
  94.        RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__); 
  95. /*return; */
  96.        } 
  97.        obj.Dump(); 
  98. #endif
  99. if (HandleInvoke(r, packet->m_body + 1, packet->m_nBodySize - 1) == 1) 
  100.       bHasMediaPacket = 2; 
  101. break; 
  102.       } 
  103. //RTMP消息类型ID=18,AMF0编码,数据消息
  104. case 0x12: 
  105. /* metadata (notify) */
  106.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__, 
  107.       packet->m_nBodySize); 
  108. //处理元数据,暂时注释
  109. /*
  110.       if (HandleMetadata(r, packet->m_body, packet->m_nBodySize))
  111.     bHasMediaPacket = 1;
  112.       break;
  113.       */
  114. //RTMP消息类型ID=19,AMF0编码,忽略
  115. case 0x13: 
  116.       RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring", 
  117.       __FUNCTION__); 
  118. break; 
  119. //RTMP消息类型ID=20,AMF0编码,命令消息
  120. //处理命令消息!
  121. case 0x14: 
  122. //----------------
  123.         r->dlg->AppendCInfo("处理收到的数据。消息 命令 (AMF0编码) (typeID=20)。"); 
  124. //-----------------------------
  125. /* invoke */
  126.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__, 
  127.       packet->m_nBodySize); 
  128.       RTMP_LogPrintf("处理命令消息 (typeID=20,AMF0编码)\n"); 
  129. /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  130. if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1) 
  131.     bHasMediaPacket = 2; 
  132. break; 
  133. //RTMP消息类型ID=22
  134. case 0x16: 
  135.       { 
  136. /* go through FLV packets and handle metadata packets */
  137.     unsigned int pos = 0; 
  138.     uint32_t nTimeStamp = packet->m_nTimeStamp; 
  139. while (pos + 11 < packet->m_nBodySize) 
  140.       { 
  141.         uint32_t dataSize = AMF_DecodeInt24(packet->m_body + pos + 1);   /* size without header (11) and prevTagSize (4) */
  142. if (pos + 11 + dataSize + 4 > packet->m_nBodySize) 
  143.           { 
  144.         RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!"); 
  145. break; 
  146.           } 
  147. if (packet->m_body[pos] == 0x12) 
  148.           { 
  149.         HandleMetadata(r, packet->m_body + pos + 11, dataSize); 
  150.           } 
  151. else if (packet->m_body[pos] == 8 || packet->m_body[pos] == 9) 
  152.           { 
  153.         nTimeStamp = AMF_DecodeInt24(packet->m_body + pos + 4); 
  154.         nTimeStamp |= (packet->m_body[pos + 7] << 24); 
  155.           } 
  156.         pos += (11 + dataSize + 4); 
  157.       } 
  158. if (!r->m_pausing) 
  159.       r->m_mediaStamp = nTimeStamp; 
  160. /* FLV tag(s) */
  161. /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
  162.     bHasMediaPacket = 1; 
  163. break; 
  164.       } 
  165. default: 
  166.       RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, 
  167.       packet->m_packetType); 
  168. #ifdef _DEBUG
  169.       RTMP_LogHex(RTMP_LOGDEBUG, (const uint8_t *)packet->m_body, packet->m_nBodySize); 
  170. #endif
  171.     } 
  172. return bHasMediaPacket; 

前文已经分析过当消息类型ID为0x14(20)的时候,即AMF0编码的命令消息的时候,会调用HandleInvoke()进行处理。

参考:RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)

这里就不再对这种类型ID的消息进行分析了,分析一下其他类型的消息,毕竟从发起一个RTMP连接到接收视音频数据这个过程中是要处理很多消息的。

参考:RTMP流媒体播放过程

下面我们按照消息ID从小到大的顺序,看看接收到的各种消息都是如何处理的。

消息类型ID是0x01的消息功能是“设置块(Chunk)大小”,处理函数是HandleChangeChunkSize(),可见函数内容很简单。

[cpp] view plaincopy

  1. static void
  2. HandleChangeChunkSize(RTMP *r, const RTMPPacket *packet) 
  3. if (packet->m_nBodySize >= 4) 
  4.     { 
  5.       r->m_inChunkSize = AMF_DecodeInt32(packet->m_body); 
  6.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, 
  7.       r->m_inChunkSize); 
  8.     } 

消息类型ID是0x03的消息功能是“致谢”,没有处理函数。

消息类型ID是0x04的消息功能是“用户控制(UserControl)”,处理函数是HandleCtrl(),这类的消息出现的频率非常高,函数体如下所示。具体用户控制消息的作用这里就不多说了,有相应的文档可以参考。

注:该函数中间有一段很长的英文注释,英语好的大神可以看一看

[cpp] view plaincopy

  1. //处理用户控制(UserControl)消息。用户控制消息是服务器端发出的。
  2. static void
  3. HandleCtrl(RTMP *r, const RTMPPacket *packet) 
  4. short nType = -1; 
  5.   unsigned int tmp; 
  6. if (packet->m_body && packet->m_nBodySize >= 2) 
  7. //事件类型(2B)
  8.     nType = AMF_DecodeInt16(packet->m_body); 
  9.   RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType, 
  10.       packet->m_nBodySize); 
  11. /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  12. if (packet->m_nBodySize >= 6) 
  13.     { 
  14. //不同事件类型做不同处理
  15. switch (nType) 
  16.     { 
  17. //流开始
  18. case 0: 
  19. //流ID
  20.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  21.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Begin %d", __FUNCTION__, tmp); 
  22. break; 
  23. //流结束
  24. case 1: 
  25. //流ID
  26.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  27.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream EOF %d", __FUNCTION__, tmp); 
  28. if (r->m_pausing == 1) 
  29.         r->m_pausing = 2; 
  30. break; 
  31. //流枯竭
  32. case 2: 
  33. //流ID
  34.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  35.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Dry %d", __FUNCTION__, tmp); 
  36. break; 
  37. //是录制流
  38. case 4: 
  39.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  40.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream IsRecorded %d", __FUNCTION__, tmp); 
  41. break; 
  42. //Ping客户端
  43. case 6:     /* server ping. reply with pong. */
  44.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  45.       RTMP_Log(RTMP_LOGDEBUG, "%s, Ping %d", __FUNCTION__, tmp); 
  46.       RTMP_SendCtrl(r, 0x07, tmp, 0); 
  47. break; 
  48. /* FMS 3.5 servers send the following two controls to let the client
  49.      * know when the server has sent a complete buffer. I.e., when the
  50.      * server has sent an amount of data equal to m_nBufferMS in duration.
  51.      * The server meters its output so that data arrives at the client
  52.      * in realtime and no faster.
  53.      *
  54.      * The rtmpdump program tries to set m_nBufferMS as large as
  55.      * possible, to force the server to send data as fast as possible.
  56.      * In practice, the server appears to cap this at about 1 hour‘s
  57.      * worth of data. After the server has sent a complete buffer, and
  58.      * sends this BufferEmpty message, it will wait until the play
  59.      * duration of that buffer has passed before sending a new buffer.
  60.      * The BufferReady message will be sent when the new buffer starts.
  61.      * (There is no BufferReady message for the very first buffer;
  62.      * presumably the Stream Begin message is sufficient for that
  63.      * purpose.)
  64.      *
  65.      * If the network speed is much faster than the data bitrate, then
  66.      * there may be long delays between the end of one buffer and the
  67.      * start of the next.
  68.      *
  69.      * Since usually the network allows data to be sent at
  70.      * faster than realtime, and rtmpdump wants to download the data
  71.      * as fast as possible, we use this RTMP_LF_BUFX hack: when we
  72.      * get the BufferEmpty message, we send a Pause followed by an
  73.      * Unpause. This causes the server to send the next buffer immediately
  74.      * instead of waiting for the full duration to elapse. (That‘s
  75.      * also the purpose of the ToggleStream function, which rtmpdump
  76.      * calls if we get a read timeout.)
  77.      *
  78.      * Media player apps don‘t need this hack since they are just
  79.      * going to play the data in realtime anyway. It also doesn‘t work
  80.      * for live streams since they obviously can only be sent in
  81.      * realtime. And it‘s all moot if the network speed is actually
  82.      * slower than the media bitrate.
  83.      */
  84. case 31: 
  85.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  86.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream BufferEmpty %d", __FUNCTION__, tmp); 
  87. if (!(r->Link.lFlags & RTMP_LF_BUFX)) 
  88. break; 
  89. if (!r->m_pausing) 
  90.         { 
  91.           r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel]; 
  92.           RTMP_SendPause(r, TRUE, r->m_pauseStamp); 
  93.           r->m_pausing = 1; 
  94.         } 
  95. else if (r->m_pausing == 2) 
  96.         { 
  97.           RTMP_SendPause(r, FALSE, r->m_pauseStamp); 
  98.           r->m_pausing = 3; 
  99.         } 
  100. break; 
  101. case 32: 
  102.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  103.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream BufferReady %d", __FUNCTION__, tmp); 
  104. break; 
  105. default: 
  106.       tmp = AMF_DecodeInt32(packet->m_body + 2); 
  107.       RTMP_Log(RTMP_LOGDEBUG, "%s, Stream xx %d", __FUNCTION__, tmp); 
  108. break; 
  109.     } 
  110.     } 
  111. if (nType == 0x1A) 
  112.     { 
  113.       RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__); 
  114. if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01) 
  115.     { 
  116.       RTMP_Log(RTMP_LOGERROR, 
  117. "%s: SWFVerification Type %d request not supported! Patches welcome...", 
  118.         __FUNCTION__, packet->m_body[2]); 
  119.     } 
  120. #ifdef CRYPTO
  121. /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  122. /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
  123. else if (r->Link.SWFSize) 
  124.     { 
  125.       RTMP_SendCtrl(r, 0x1B, 0, 0); 
  126.     } 
  127. else
  128.     { 
  129.       RTMP_Log(RTMP_LOGERROR, 
  130. "%s: Ignoring SWFVerification request, use --swfVfy!", 
  131.           __FUNCTION__); 
  132.     } 
  133. #else
  134.       RTMP_Log(RTMP_LOGERROR, 
  135. "%s: Ignoring SWFVerification request, no CRYPTO support!", 
  136.       __FUNCTION__); 
  137. #endif
  138.     } 

消息类型ID是0x05的消息功能是“窗口致谢大小(Window Acknowledgement Size,翻译的真是挺别扭)”,处理函数是HandleServerBW()。在这里注意一下,该消息在Adobe官方公开的文档中叫“Window Acknowledgement Size”,但是在Adobe公开协议规范之前,破解RTMP协议的组织一直管该协议叫“ServerBW”,只是个称呼,倒是也无所谓~处理代码很简单:

[cpp] view plaincopy

  1. static void
  2. HandleServerBW(RTMP *r, const RTMPPacket *packet) 
  3.   r->m_nServerBW = AMF_DecodeInt32(packet->m_body); 
  4.   RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r->m_nServerBW); 

消息类型ID是0x06的消息功能是“设置对等端带宽(Set Peer Bandwidth)”,处理函数是HandleClientBW()。与上一种消息一样,该消息在Adobe官方公开的文档中叫“Set Peer Bandwidth”,但是在Adobe公开协议规范之前,破解RTMP协议的组织一直管该协议叫“ClientBW”。处理函数也不复杂:

[cpp] view plaincopy

  1. static void
  2. HandleClientBW(RTMP *r, const RTMPPacket *packet) 
  3.   r->m_nClientBW = AMF_DecodeInt32(packet->m_body); 
  4. if (packet->m_nBodySize > 4) 
  5.     r->m_nClientBW2 = packet->m_body[4]; 
  6. else
  7.     r->m_nClientBW2 = -1; 
  8.   RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r->m_nClientBW, 
  9.       r->m_nClientBW2); 

消息类型ID是0x08的消息用于传输音频数据,在这里不处理。

消息类型ID是0x09的消息用于传输音频数据,在这里不处理。

消息类型ID是0x0F-11的消息用于传输AMF3编码的命令。

消息类型ID是0x12-14的消息用于传输AMF0编码的命令。

注:消息类型ID是0x14的消息很重要,用于传输AMF0编码的命令,已经做过分析。

转:RTMPDump源代码分析

标签:

原文地址:http://www.cnblogs.com/xkfz007/p/4520897.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!