分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.youkuaiyun.com/jiangjunshow
=====================================================
RTMPdump(libRTMP) 源代码分析系列文章:
RTMPDump (libRTMP) 源代码分析2:解析RTMP地址——RTMP_ParseURL()
RTMPdump (libRTMP) 源代码分析3: AMF编码
RTMPdump (libRTMP) 源代码分析4: 连接第一步——握手 (HandShake)
RTMPdump (libRTMP) 源代码分析5: 建立一个流媒体连接 (NetConnection部分)
RTMPdump (libRTMP) 源代码分析6: 建立一个流媒体连接 (NetStream部分 1)
RTMPdump (libRTMP) 源代码分析7: 建立一个流媒体连接 (NetStream部分 2)
RTMPdump (libRTMP) 源代码分析8: 发送消息 (Message)
RTMPdump (libRTMP) 源代码分析9: 接收消息 (Message) (接收视音频数据)
RTMPdump (libRTMP) 源代码分析10: 处理各种消息 (Message)
=====================================================
函数调用结构图
RTMPDump (libRTMP)的整体的函数调用结构图如下图所示。
详细分析
前一篇文章分析了RTMPdump(libRTMP) 的发送消息(Message)方面的源代码:RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)
在这里在研究研究接收消息(Message)的源代码,接收消息最典型的应用就是接收视音频数据了,因为视频和音频分别都属于RTMP协议规范中的一种消息。在这里主要分析接收视音频数据。
RTMPdump中完成视音频数据的接收(也可以说是视音频数据的下载)的函数是:RTMP_Read()。
RTMPdump主程序中的Download()函数就是通过调用RTMP_Read()完成数据接收,从而实现下载的。
那么我们马上开始吧,首先看看RTMP_Read()函数:
//FLV文件头static const char flvHeader[] = { 'F', 'L', 'V', 0x01, 0x00, /* 0x04代表有音频, 0x01代表有视频 */ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00};#define HEADERBUF (128*1024)intRTMP_Read(RTMP *r, char *buf, int size){ int nRead = 0, total = 0; /* can't continue */fail: switch (r->m_read.status) { case RTMP_READ_EOF: case RTMP_READ_COMPLETE: return 0; case RTMP_READ_ERROR: /* corrupted stream, resume failed */ SetSockError(EINVAL); return -1; default: break; } /* first time thru */ if (!(r->m_read.flags & RTMP_READ_HEADER)) { if (!(r->m_read.flags & RTMP_READ_RESUME)) { //分配内存,指向buf的首部和尾部 char *mybuf = (char *) malloc(HEADERBUF), *end = mybuf + HEADERBUF; int cnt = 0; //buf指向同一地址 r->m_read.buf = mybuf; r->m_read.buflen = HEADERBUF; //把Flv的首部复制到mybuf指向的内存 //RTMP传递的多媒体数据是“砍头”的FLV文件 memcpy(mybuf, flvHeader, sizeof(flvHeader)); //m_read.buf指针后移flvheader个单位 r->m_read.buf += sizeof(flvHeader); //buf长度增加flvheader长度 r->m_read.buflen -= sizeof(flvHeader); //timestamp=0,不是多媒体数据 while (r->m_read.timestamp == 0) { //读取一个Packet,到r->m_read.buf //nRead为读取结果标记 nRead = Read_1_Packet(r, r->m_read.buf, r->m_read.buflen); //有错误 if (nRead < 0) { free(mybuf); r->m_read.buf = NULL; r->m_read.buflen = 0; r->m_read.status = nRead; goto fail; } /* buffer overflow, fix buffer and give up */ if (r->m_read.buf < mybuf || r->m_read.buf > end) { mybuf = (char *) realloc(mybuf, cnt + nRead); memcpy(mybuf+cnt, r->m_read.buf, nRead); r->m_read.buf = mybuf+cnt+nRead; break; } // //记录读取的字节数 cnt += nRead; //m_read.buf指针后移nRead个单位 r->m_read.buf += nRead; r->m_read.buflen -= nRead; //当dataType=00000101时,即有视频和音频时 //说明有多媒体数据了 if (r->m_read.dataType == 5) break; } //读入数据类型 //注意:mybuf指针位置一直没动 //mybuf[4]中第 6 位表示是否存在音频Tag。第 8 位表示是否存在视频Tag。 mybuf[4] = r->m_read.dataType; //两个指针之间的差 r->m_read.buflen = r->m_read.buf - mybuf; r->m_read.buf = mybuf; //这句很重要!后面memcopy r->m_read.bufpos = mybuf; } //flags标明已经读完了文件头 r->m_read.flags |= RTMP_READ_HEADER; } if ((r->m_read.flags & RTMP_READ_SEEKING) && r->m_read.buf) { /* drop whatever's here */ free(r->m_read.buf); r->m_read.buf = NULL; r->m_read.bufpos = NULL; r->m_read.buflen = 0; } /* If there's leftover data buffered, use it up */ if (r->m_read.buf) { nRead = r->m_read.buflen; if (nRead > size) nRead = size; //m_read.bufpos指向mybuf memcpy(buf, r->m_read.bufpos, nRead); r->m_read.buflen -= nRead; if (!r->m_read.buflen) { free(r->m_read.buf); r->m_read.buf = NULL; r->m_read.bufpos = NULL; } else { r->m_read.bufpos += nRead; } buf += nRead; total += nRead; size -= nRead; } //接着读 while (size > 0 && (nRead = Read_1_Packet(r, buf, size)) >= 0) { if (!nRead) continue; buf += nRead; total += nRead; size -= nRead; break; } if (nRead < 0) r->m_read.status = nRead; if (size < 0) total += size; return total;}
程序关键的地方都已经注释上了代码,在此就不重复说明了。有一点要提一下:RTMP传送的视音频数据的格式和FLV(FLash Video)格式是一样的,把接收下来的数据直接存入文件就可以了。但是这些视音频数据没有文件头,是