文章目录

  • librtmp库API介绍及其结构概述
    • 1、librtmp API的使用
      • 1.1 librtmp API的使用流程
      • 1.2 librtmp的实例
      • 1.3 librtmp API详解
        • 1.3.1 librtmp中的chunk和message
        • 1.3.2 RTMP基本API
        • 1.3.3 设置RTMP的属性
        • 1.3.4 连接服务器
        • 1.3.5 创建rtmp流
        • 1.3.6 对RTMP流的操作
          • 1.3.6.1 控制类API
          • 1.3.6.2 查询类API
          • 1.3.6.3 读写类API
    • 2. librtmp数据流的读写流程
      • 2.1 传输层
        • 2.1.1 TCP接口
      • 2.1.2 HTTP层接口
      • 2.2 Message读写层
      • 2.3 业务层
      • 2.4 FLV读写层
      • 2.5 其他:处理Message以及发送Message
    • 3.rtmpdump示例
    • 4. 参考文章
    • 5. 注释代码

librtmp库API介绍及其结构概述

​ rtmp是常见的一种流媒体协议,它是由Adobe公司提出的一种应用层协议。rtmp传输的是flv格式的封装数据,flv中保存的一般是H.264视频流和AAC音频流。

​ librtmp库实现了rtmp协议的客户端功能,以及少数服务端功能,是学习rtmp的一个良好的工具。

​ 本文针对librtmp的主要API做了介绍,并整理了librtmp的大致实现框架,但并没有详细深入到具体细节。本文并非入门类的文章,而是一个提纲挈领性的总结,希望对想要使用librtmp以及学历librtmp的人有所帮助。

​ 在想要深入理解librtmp是如何工作之前,请先对FLV封装格式和rtmp协议做相关了解

1、librtmp API的使用

1.1 librtmp API的使用流程

​ librtmp API的使用流程可以概括为如下几步:

  1. 分配并初始化一个RTMP实例

  2. 设置RTMP实例的参数

  3. 调用RTMP_Connect函数连接到服务器

  4. 调用RTMP_ConnectStream函数创建一个rtmp流

  5. 使用RTMP_Read或RTMP_Write进行读写,或者对rtmp流进行控制(如暂停、停止等)

  6. 读写完成后,关闭并释放RTMP

​ 在librtmp中,使用结构体RTMP表示一个rtmp连接。

​ 在第2步中根据自己的需要对rtmp连接做相应的设置,后续librtmp就会根据我们的设置做对应的操作。注意这里仅仅是设置了参数,这些参数尚未被使用。考察以下两个接口:

//设置缓冲的时长,单位:毫秒
void RTMP_SetBufferMS(RTMP *r, int size);
//发送“设置缓冲时长”的控制包
void RTMP_UpdateBufferMS(RTMP *r);

​ 前者仅是将size这个参数保存到实例r中,后者则是使用实例r中参数并起到实际的作用。

​ 因此,要注意:一般我们仅在第2步进行参数设置操作,不建议在其他地方进行参数设置,这是因为设置参数和这个参数实际起到相关作用是两个概念。当然,如果你非常了解librtmp的工作方式,也可以在其他地方进行设置。

​ 在连接到rtmp服务器,并建立一个rtmp流之后,我们就可以对这个流进行相关的操作。例如,可以进行暂停、停止等操作,也可以读取或者写入到rtmp流。

​ 等相关操作完成之后,必须关闭并释放RTMP实例。

1.2 librtmp的实例

​ 我们首先查看两个使用librtmp API的例子,了解一下其API以及使用流程。

​ 首先,是读取rtmp流另存为flv文件的示例:

https://blog.csdn.net/leixiaohua1020/article/details/42104893

​ 然后是将本地文件上传到rtmp服务器的示例:

https://blog.csdn.net/leixiaohua1020/article/details/42104945

​ 在详细了解了上述两个例子后,就可以详细系统地学习librtmp的API。

1.3 librtmp API详解

​ 通过上述的示例我们知道RTMP类型是librtmp中非常重要的一个类型,我们现在查看一下它的相关API。

​ 先不必查看RTMP的具体组成,我们只需要知道它代表一个rtmp流就可以了。下面就是RTMP相关的最基本的API。

​ 这些基本API包括有:分配,初始化,关闭rtmp流,释放。

1.3.1 librtmp中的chunk和message

​ 首先,我们了解一下librtmp中chunk和Message相关的结构体,以及其相关的操作。

​ 以下分别对应chunk和Mesage的结构体:

/*表示一个 raw chunk,原始chunkc_header,c_headerSize:保存chunk header的数据和大小c_chunk,c_chunkSize:保存chunk data的数据和大小
*/
typedef struct RTMPChunk
{int c_headerSize;int c_chunkSize;char *c_chunk;char c_header[RTMP_MAX_HEADER_SIZE];
} RTMPChunk;/*表示一个Messagem_headerType        :表示m_chunk的类型,即chunk header中的basic header中的fmtm_packetType        :表示Message Type IDm_hasAbsTimestamp   :表示时间戳是绝对的还是相对的,即chunk type为0时为绝对时间戳,其他类型时为时间戳增量m_nChannel          :表示chunk Stream IDm_nTimeStamp        :时间戳m_nInfoField2       :chunk fmt为0时,header的最后四个字节,即Message Stream IDm_nBodySize         :Message的body的尺寸m_nBytesRead        :已经读取到的body的字节数m_chunk             :如果不为NULL,表示用户想要获取chunk,那么在读取Message时,会填充这个字段m_body              :Message的body
*/
typedef struct RTMPPacket
{uint8_t m_headerType;uint8_t m_packetType;uint8_t m_hasAbsTimestamp;   /* timestamp absolute or relative? */int m_nChannel;uint32_t m_nTimeStamp;  /* timestamp */int32_t m_nInfoField2;   /* last 4 bytes in a long header */uint32_t m_nBodySize;uint32_t m_nBytesRead;RTMPChunk *m_chunk;char *m_body;
} RTMPPacket;//将全部字段置空(缓冲区仍在)
void RTMPPacket_Reset(RTMPPacket *p);
//打印packet
void RTMPPacket_Dump(RTMPPacket *p);
//为p分配缓冲区,其中包括固定大小的header,nSize大小的body
int RTMPPacket_Alloc(RTMPPacket *p, int nSize);
//释放缓冲区
void RTMPPacket_Free(RTMPPacket *p);//packet是否完备
#define RTMPPacket_IsReady(a)   ((a)->m_nBytesRead == (a)->m_nBodySize)

​ 注意以下几点:

  • chunk仅有一个保存其原始内容的结构体,而没有详细的相关字段的结构体;
  • message中的字段名有些似是而非,而且所包含的内容似乎和chunk的内容有所混淆,这是因为librtmp是逆向工程的产物,作者是根据实际rtmp的数据包猜测rtmp协议的内容,逆向解析rtmp协议,当时Adobe尚未公布rtmp协议的内容;

1.3.2 RTMP基本API

//分配一个RTMP
//其操作仅仅是分配一块RTMP大小的内存而已,RTMP也可以在栈上分配
RTMP *RTMP_Alloc(void);//初始化RTMP,将RTMP各个字段设置为默认值
void RTMP_Init(RTMP *r);//关闭这个RTMP,如果当前处于连接状态,则会关闭连接;然后释放RTMP相关的内容
void RTMP_Close(RTMP *r);//释放RTMP的内存
void RTMP_Free(RTMP *r);

1.3.3 设置RTMP的属性

​ 分配并初始化一个RTMP实例之后,就需要设置RTMP实例的属性,以下就是设置属性相关的API。

//解析RTMP地址
//url的格式为:protocol://host:port/app/playpath,将解析后的字段通过参数返回
int RTMP_ParseURL(const char *url, int *protocol, AVal *host,unsigned int *port, AVal *playpath, AVal *app);//从RTMP URL中解析playpath
void RTMP_ParsePlaypath(AVal *in, AVal *out);//设置缓冲的时长,单位:毫秒
void RTMP_SetBufferMS(RTMP *r, int size);//设置RTMP的相关属性
//如果opt指定的属性不存在,那么会调用RTMP_OptUsage()打印出RTMP所有的属性。
int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg);//打印rtmp所有的属性
static void RTMP_OptUsage();//设置url
int RTMP_SetupURL(RTMP *r, char *url);//设置流
void RTMP_SetupStream(RTMP *r, int protocol,  AVal *hostname,                 unsigned int port,AVal *sockshost,AVal *playpath,AVal *tcUrl,AVal *swfUrl,AVal *pageUrl,AVal *app,AVal *auth,AVal *swfSHA256Hash,uint32_t swfSize,AVal *flashVer,AVal *subscribepath,int dStart,int dStop, int bLiveStream, long int timeout);//设置RTMP Link添加写属性
//即要pulish一个流而不是play一个流
void RTMP_EnableWrite(RTMP *r);

​ 首先,RTMP_ParseURL() 和 RTMP_ParsePlaypath() 是两个辅助函数,它们用于将形似protocol://host:port/app/playpath 的字符串,解析为对应的各个字段,然后再设置给RTMP示例。

​ 后面5个函数才是设置属性的函数。其中,RTMP_SetBufferMS() 和 RTMP_EnableWrite() 只能设置单个相关的属性:前者设置“缓冲时长”,后者告诉librtmp我们要使用polish而非play,即我们要推流到服务器而不是从服务器拉流。

​ 而剩下的三个函数在本质上是一样的,没有任何区别,只是为设置属性提供了不同形式的接口而已。RTMP_SetOpt() 会根据opt来设置不同的属性;RTMP_SetupURL() 则在底层会调用 RTMP_ParseURL() 函数,然后将解析出来的各个字段分别设置为RTMP实例的不同属性;RTMP_SetupStream() 则提供了一个方便的接口,让我们可以一次性设置全部的属性。

​ 那么,RTMP具体可以设置哪些属性呢?我们不在这里赘述,请查看rtmpdump的介绍,其中详细介绍了RTMP的相关属性。

1.3.4 连接服务器

/*客户端发起一个和服务器的TCP连接,并执行Connect内部调用了RTMP_Connect0来完成TCP层面的连接调用RTMP_Connect1完成了Connect
*/
int RTMP_Connect(RTMP *r, RTMPPacket *cp);
struct sockaddr;
//将host和port信息写入到sockaddr_in中
static int add_addr_info(struct sockaddr_in *service, AVal *host, int port);
//连接到指定地址(服务器地址或代理地址),仅在TCP层
int RTMP_Connect0(RTMP *r, struct sockaddr *svc);
//假定TCP层面的连接已经完成
//进行handshake以及发送Connect Message
int RTMP_Connect1(RTMP *r, RTMPPacket *cp);//服务端处理handshake,成功返回值大于0
int RTMP_Serve(RTMP *r);

​ 从用户的角度而言,这部分没什么内容,仅需知道 RTMP_Connect() 函数可以连接到rtmp服务器即可。

1.3.5 创建rtmp流

//连接到url指定的流,返回TRUE和FLASE
int RTMP_ConnectStream(RTMP *r, int seekTime);

​ 这一步也比较简单,仅需注意在调用它之前,设置足够的属性即可。尤其是如下两点:

  • 设置正确的的url信息
  • 如果是推流,即polish,那么调用 RTMP_EnableWrite() ,如果是播放一个流,即play,那么不需要额外设置,默认就是play操作。

1.3.6 对RTMP流的操作

​ rtmp流创建完成之后,就可以针对这个流做相关的操作。针对RTMP流的操作,可以分为三大类:一类是控制操作,如暂停、停止、恢复、重新连接、中断等等操作;第二类是查询类接口,返回rtmp流的当前状态;最后一类则是读写操作,用于读取或者写入rtmp流数据。

1.3.6.1 控制类API
//发送“设置缓冲时间”的控制包
void RTMP_UpdateBufferMS(RTMP *r);
//暂停或者恢复流
int RTMP_ToggleStream(RTMP *r);
//重新连接流,即delete Stream之后再次Connect Stream,且同步等待到流开始
int RTMP_ReconnectStream(RTMP *r, int seekTime);
//停止流,或者删除流
void RTMP_DeleteStream(RTMP *r);
//RTMP流程被打断
//本质为设置全局变量RTMP_ctrlC为true
void RTMP_UserInterrupt(void);  /* user typed Ctrl-C */

​ 所谓控制类的API,就是会对当前rtmp流产生改变的API。从用户角度而言,一般就是对rtmp流进行暂停、恢复、停止等操作,而在rtmp协议层面,还会有其他的操作。在这里我们仅关注前者,无论是那种API,它们都是通过发送一个RTMP Msg来实现的。

1.3.6.2 查询类API
//RTMP是否处于连接状态
int RTMP_IsConnected(RTMP *r);
//返回RTMP关联的套接字
int RTMP_Socket(RTMP *r);
//RTMP是否中断
int RTMP_IsTimedout(RTMP *r);
//获得RTMP当前流的长度
double RTMP_GetDuration(RTMP *r);

​ 查看当前rtmp流的相关状态或者信息。

1.3.6.3 读写类API
//RTMP读取到一个packet,并不保证一定读取到完整的packet
int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
//使用RTMP发送packet,queue表示是否按照队列发送
int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);
//RTMP发送一个chunk
int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk);//使用RTMP读取数据
int RTMP_Read(RTMP *r, char *buf, int size);
//使用RTMP写入数据
int RTMP_Write(RTMP *r, const char *buf, int size);

​ librtmp中的读写的接口非常多,如果不了解的话,根本不知道应该调用哪些函数来完成我们的功能。

​ 以上是我们最常用的5个读写API,先暂时记住,会使用即可。我们在下面会详细介绍librtmp的读写流程。

2. librtmp数据流的读写流程

​ 我们只有全面了解了librtmp库的数据解析流程,才能对这个库有一个整体的把握,从而更合理的使用这个库。并通过这个过程印证学习RTMP协议,这才是最重要的。

​ 我根据自己的理解,将数据解析的流程分为多个层次,下面我们按照从底到高的顺序一一了解其解析流程。

2.1 传输层

​ 标准的rtmp协议在底层使用TCP协议传输数据,而有的一些变种rtmp协议则可能会使用HTTP协议来传输数据。无论使用那种协议,这部分都有且仅有数据传输的功能,因此我们将其称为传输层。

​ 传输层对上提供了一组统一的接口,以封装其传输协议的不同,接口如下所示:

//读取N个字节,返回实际读取到的字节数
static int ReadN(RTMP *r, char *buffer, int n);//写入N个字节,返回实际写入的字节数
static int WriteN(RTMP *r, const char *buffer, int n);

​ 而传输层则依赖更底层的TCP接口和HTTP接口。

2.1.1 TCP接口

​ TCP层相关的定义如下,较为简单:

/*RTMPSockBuf:RTMP的套接字缓存表示一个TCP套接字连接,以及其读取缓存sb_socket   :Socket套接字sb_size     :buffer中未处理的字节数量,即缓冲数据的大小sb_start    :指向buffer中需要处理的字节,即指向缓冲数据sb_buf      :数据读取缓冲区sb_timedout :套接字是否中断sb_ssl      :SSL相关数据
*/
typedef struct RTMPSockBuf
{int sb_socket;int sb_size;     /* number of unprocessed bytes in buffer */char *sb_start;      /* pointer into sb_pBuffer of next byte to process */char sb_buf[RTMP_BUFFER_CACHE_SIZE];   /* data read from socket */int sb_timedout;void *sb_ssl;
} RTMPSockBuf;//TCP层面的操作:读、写、关闭
int RTMPSockBuf_Fill(RTMPSockBuf *sb);
int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len);
int RTMPSockBuf_Close(RTMPSockBuf *sb);

2.1.2 HTTP层接口

​ HTTP层的接口也相对简单,如下所示:

//HTTP层面的操作:读,写
static int HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len);
static int HTTP_read(RTMP *r, int fill);

2.2 Message读写层

​ 这个层在传输层之上,它的作用是读写Message和Chunk。

​ 这个层是非常单纯的读写的层,它直接解析从传输层读取到的数据,而不处理解析出来的Message和Chunk,它也不管要写入的数据的内容是什么,直接调用传输层接口写入相关数据。

​ 特点:解析数据,但不处理数据的内容,而是交给更上层处理。

/*作用:读取一个chunk,然后查看chunk是否能合成一个完整的Message默认条件:函数调用前后,当前流的第一个字节为新的Chunk的开始字节如果packet读取完毕,通过packet可以获取到相关的数据如果packet尚未读取完毕,不能通过packet获取到相关的数据,但可以获取相关的情况
*/
int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);/*将msg拆分为chunk后,写入chunk1 分析msg所使用的chunk的type2 拆分msg为chunk   3 当msg为command msg时,处理相关字段底层调用WriteN()函数,而不是RTMP_SendPacket()函数
*/
int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);/*直接写入chunk,但rtmp库中并没有使用这个函数,建议用户也不要使用内容:仅仅是写入chunk底层调用WriteN()函数
*/
int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk)

2.3 业务层

​ 在Message读写层之上,业务层对其进行了相关且必要的业务处理,具体包括:

  • 对服务器发送的非media Message的处理
  • 过滤非media Message
  • 处理seek操作
  • 处理resume操作
  • 过滤尺寸过小的video和audio rtmp Message

​ 这样,经过业务层后,返回的就是用户想要获取的媒体数据和元对象数据了。这里所谓的“用户想要获取”,包含了用户指定的seek和resume操作。

/* Read from the stream until we get a media packet.* Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media* packets, 0 if ignorable error, >0 if there is a media packet** 从流中读取数据,直到获取到一个媒体packet* 什么是媒体packet,请查看RTMP_GetNextMediaPacket()函数* 返回值:表示当前RTMP流的状态,即RTMP_READ.status,如下:*          有可忽略的错误返回0,读取到了媒体packet返回正值** 作用:处理设置后的RTMP连接,并返回媒体RTMPPacket** 1 处理seek* 2 处理resume* 3 过滤非media rtmp packet(通过调用 RTMP_GetNextMediaPacket() 完成)* 4 过滤尺寸太小的video audio rtmppacket*/
static int Read_1_Packet(RTMP *r, char *buf, unsigned int buflen);/*获取一个Media message忽略不是Media Message的Message,直到获取media message。这个函数会调用RTMP_ReadPacket函数读取一个Message,然后调用RTMP_ClientPacket处理读取到的Message。什么是media Message,详情查看RTMP_ClientPacket
*/
int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet)/*客户端处理Message返回值:1表示packet为audio,video,metadata message(object msg中的一种),聚合 message2表示packet为command message,且为Play.Complete或Play.Stop等表示流暂停或结束0表示其他message这个函数通过调用下面的handle message块,完成对各种类型Message的调用当返回值大于0时,表示这个packet是一个media packet,即这个packet真的包含媒体数据,或者是一些表示媒体已经结束或暂停,暂时不可能获取到真的媒体数据,因此返回,防止逻辑卡死在这里。
*/
int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet)

2.4 FLV读写层

​ rtmp流经过业务层处理后,就剩下媒体数据和元对象数据了,但这些数据仍然是以Message形式存在的,而有时,我们需要以字节流的形式,即flv格式的字节流,来保存媒体数据,因此就有了FLV读写层,其作用如下:

  • Read时,将Message格式的媒体数据转化为为FLV TAG格式;Write时,相反
  • Read时,补全FLV Header部分;Write时,跳过FLV Header部分

​ 这也是为什么上述第一个rtmp示例,将rtmp流保存为flv文件,使用FLV读写层接口的原因;而且在第二个示例中,也有分别使用FLV读写层和Message读写层API的不同示例,其最大的不同就在于一个是FLV TAG形式的字节流形式,一个是RTMPPacket结构的Message形式。

/*读取数据,FLV层级返回的值为一个FLV数据流,正如RTMP_Write写入一个FLV数据流1 完成 FLV TAG ==> RTMPPacket,RTMP_Write则相反2 补全FLV数据流,如加入FLV Header返回值:0   表示已经完成读取RTMP流-1  表示发生了错误>0  表示实际读取的字节数
*/
int RTMP_Read(RTMP *r, char *buf, int size)/*依次写入FLV文件的一部分。注意:一般不能遗漏FLV文件的任何部分。当然,如果想要忽略一部分数据,那么可以忽略【FLVHeader+第一个preTagSize】或者任意的【FLVTAG+preTagSize】实际写入的部分为:FLV TAG,其他的部分会被忽略,其中TAGHeader会被解析到Msg Header,TAGBody为Msg Body。RTMP_Write将传入的数据封装为RTMPPacket,然后调用RTMP_SendPacket()函数发送。返回值:成功返回实际写入的字节数。失败返回-1。
*/
int RTMP_Write(RTMP *r, const char *buf, int size)

2.5 其他:处理Message以及发送Message

​ 在上面的业务层中,当读取到一个Message之后,会调用RTMP_ClientPacket()来处理读取到的Message,在这个函数内部,根据Message的类型会调用不同的处理函数,也就是下面形为 “Handle***()”的函数。

​ 请注意,这里这些处理函数的命名方式,它的命名说明了这些函数都是框架内部的函数,而不是对外的接口。

​ 如果对rtmp协议感兴趣,可以详细查看这些函数的内容,来印证学习相关的协议内容。

static int HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize);
static int HandleMetadata(RTMP *r, char *body, unsigned int len);
static void HandleChangeChunkSize(RTMP *r, const RTMPPacket *packet);
static void HandleAudio(RTMP *r, const RTMPPacket *packet);
static void HandleVideo(RTMP *r, const RTMPPacket *packet);
static void HandleCtrl(RTMP *r, const RTMPPacket *packet);
static void HandleServerBW(RTMP *r, const RTMPPacket *packet);
static void HandleClientBW(RTMP *r, const RTMPPacket *packet);

​ 与上面相对应的,当我们想要对rtmp流做一定的修改时,就需要往对方发送Message数据包。具体发送数据包的接口如下,它们都是形如 “Send***()” 或者形如 “RTMP_Send***()” 的函数,当然在librtmp中,还有其他构筑在这些具体发送Message数据包的接口之上的其他接口,当我们遇到时,应该能改轻易分辨出来。

​ 同上,如果对rtmp协议的具体内容感兴趣,可以取查看这些函数的细节。

static int SendConnectPacket(RTMP *r, RTMPPacket *cp);
static int SendCheckBW(RTMP *r);
static int SendCheckBWResult(RTMP *r, double txn);
static int SendDeleteStream(RTMP *r, double dStreamId);
static int SendReleaseStream(RTMP *r);
static int SendFCPublish(RTMP *r);
static int SendPublish(RTMP *r);
static int SendFCUnpublish(RTMP *r);
static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
static int SendPlay(RTMP *r);
static int SendBytesReceived(RTMP *r);
int RTMP_SendCreateStream(RTMP *r);
int RTMP_SendPause(RTMP *r, int DoPause, int iTime)
int RTMP_SendSeek(RTMP *r, int iTime);
int RTMP_SendServerBW(RTMP *r);
int RTMP_SendClientBW(RTMP *r);
int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime);

3.rtmpdump示例

​ 这个示例全方位展示了如何使用librtmp作为客户端使用,如果不知道如何使用librtmp,可以参考这个示例。

4. 参考文章

​ 本文仅是一个提纲类的文档,远远没有将librtmp的各个细节讲解清除,读者可以去参考其他人的相关文章,其中雷神的librtmp相关的系列文章非常详细,是不可多得的好资料,地址如下:https://blog.csdn.net/leixiaohua1020/article/details/15814587。

5. 注释代码

​ 以下我注释的rtmp.h文件:

#ifndef __RTMP_H__
#define __RTMP_H__
/**      Copyright (C) 2005-2008 Team XBMC*      http://www.xbmc.org*      Copyright (C) 2008-2009 Andrej Stepanchuk*      Copyright (C) 2009-2010 Howard Chu**  This file is part of librtmp.**  librtmp is free software; you can redistribute it and/or modify*  it under the terms of the GNU Lesser General Public License as*  published by the Free Software Foundation; either version 2.1,*  or (at your option) any later version.**  librtmp is distributed in the hope that it will be useful,*  but WITHOUT ANY WARRANTY; without even the implied warranty of*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the*  GNU General Public License for more details.**  You should have received a copy of the GNU Lesser General Public License*  along with librtmp see the file COPYING.  If not, write to*  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,*  Boston, MA  02110-1301, USA.*  http://www.gnu.org/copyleft/lgpl.html*/#if !defined(NO_CRYPTO) && !defined(CRYPTO)
#define CRYPTO
#endif#include <errno.h>
#include <stdint.h>
#include <stddef.h>#include "amf.h"#ifdef __cplusplus
extern "C"
{#endif//这个库的版本号
#define RTMP_LIB_VERSION    0x020300    /* 2.3 *///rtmp的特征 FEATURE
#define RTMP_FEATURE_HTTP   0x01
#define RTMP_FEATURE_ENC    0x02
#define RTMP_FEATURE_SSL    0x04
#define RTMP_FEATURE_MFP    0x08    /* not yet supported */
#define RTMP_FEATURE_WRITE  0x10    /* publish, not play */     //推流而非播放
#define RTMP_FEATURE_HTTP2  0x20    /* server-side rtmpt */     //服务器端的rtmpt//rtmp及其变种的协议类型,本质为上述FEATURE的组合
#define RTMP_PROTOCOL_UNDEFINED -1
#define RTMP_PROTOCOL_RTMP      0
#define RTMP_PROTOCOL_RTMPE     RTMP_FEATURE_ENC
#define RTMP_PROTOCOL_RTMPT     RTMP_FEATURE_HTTP
#define RTMP_PROTOCOL_RTMPS     RTMP_FEATURE_SSL
#define RTMP_PROTOCOL_RTMPTE    (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC)
#define RTMP_PROTOCOL_RTMPTS    (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL)
#define RTMP_PROTOCOL_RTMFP     RTMP_FEATURE_MFP//默认的max chunk size
#define RTMP_DEFAULT_CHUNKSIZE  128//SocketBuffer层面接收缓冲区的尺寸
/* needs to fit largest number of bytes recv() may return */
#define RTMP_BUFFER_CACHE_SIZE (16*1024)//chunk Stream id所能表达的最大值
#define RTMP_CHANNELS   65600//rtmp及其变种的协议类型的字符串名称,小写
//还有一个对应的大写的版本,在rtmp.c中定义,不对用户开放
extern const char RTMPProtocolStringsLower[][7];
//默认的Flash版本号字符串描述
extern const AVal RTMP_DefaultFlashVer;
//表示ctrl-c中断机制
extern int RTMP_ctrlC;//获取当前时间
uint32_t RTMP_GetTime(void);//Message Type ID
#define RTMP_PACKET_TYPE_AUDIO 0x08 //audio message
#define RTMP_PACKET_TYPE_VIDEO 0x09 //video message
#define RTMP_PACKET_TYPE_INFO  0x12 //command message//chunk header由三部分组成,各个部分的尺寸均可变化
//其最大尺寸时各个部分的字节为:3+11+4
#define RTMP_MAX_HEADER_SIZE 18//chunk header的第一部分basic header中的fmt取值
#define RTMP_PACKET_SIZE_LARGE    0
#define RTMP_PACKET_SIZE_MEDIUM   1
#define RTMP_PACKET_SIZE_SMALL    2
#define RTMP_PACKET_SIZE_MINIMUM  3/*表示一个 raw chunk,原始chunkc_header,c_headerSize:保存chunk header的数据和大小c_chunk,c_chunkSize:保存chunk data的数据和大小
*/
typedef struct RTMPChunk
{int c_headerSize;int c_chunkSize;char *c_chunk;char c_header[RTMP_MAX_HEADER_SIZE];
} RTMPChunk;/*表示一个Messagem_headerType        :表示m_chunk的类型,即chunk header中的basic header中的fmtm_packetType        :表示Message Type IDm_hasAbsTimestamp   :表示时间戳是绝对的还是相对的,即chunk type为0时为绝对时间戳,其他类型时为时间戳增量m_nChannel          :表示chunk Stream IDm_nTimeStamp        :时间戳m_nInfoField2       :chunk fmt为0时,header的最后四个字节,即Message Stream IDm_nBodySize         :Message的body的尺寸m_nBytesRead        :已经读取到的body的字节数m_chunk             :如果不为NULL,表示用户想要获取chunk,那么在读取Message时,会填充这个字段m_body              :Message的body
*/
typedef struct RTMPPacket
{uint8_t m_headerType;uint8_t m_packetType;uint8_t m_hasAbsTimestamp;   /* timestamp absolute or relative? */int m_nChannel;uint32_t m_nTimeStamp;  /* timestamp */int32_t m_nInfoField2;   /* last 4 bytes in a long header */uint32_t m_nBodySize;uint32_t m_nBytesRead;RTMPChunk *m_chunk;char *m_body;
} RTMPPacket;/*RTMPSockBuf:RTMP的传输层的套接字及其缓存表示一个TCP套接字连接,以及其读取缓存sb_socket   :Socket套接字sb_size     :buffer中未处理的字节数量,即缓冲数据的大小sb_start    :指向buffer中需要处理的字节,即指向缓冲数据sb_buf      :数据读取缓冲区sb_timedout :套接字是否中断sb_ssl      :SSL相关数据
*/
typedef struct RTMPSockBuf
{int sb_socket;int sb_size;     /* number of unprocessed bytes in buffer */char *sb_start;      /* pointer into sb_pBuffer of next byte to process */char sb_buf[RTMP_BUFFER_CACHE_SIZE];   /* data read from socket */int sb_timedout;void *sb_ssl;
} RTMPSockBuf;//将全部字段置空(缓冲区仍在)
void RTMPPacket_Reset(RTMPPacket *p);
//打印packet
void RTMPPacket_Dump(RTMPPacket *p);
//为p分配缓冲区,其中包括固定大小的header,nSize大小的body
int RTMPPacket_Alloc(RTMPPacket *p, int nSize);
//释放缓冲区
void RTMPPacket_Free(RTMPPacket *p);//packet是否完备
#define RTMPPacket_IsReady(a)   ((a)->m_nBytesRead == (a)->m_nBodySize)/*RTMP的连接参数,即要建立RTMP连接所需的参数集注意:这是由客户端的用户提供的这个结构体里的字段的含义和rtmpdump中的选项联系紧密。可以查看rtmpdump中选项的含义来帮助我们理解它们。
*/
typedef struct RTMP_LNK
{AVal hostname;      //要连接的服务器的主机名AVal sockshost;     //代理主机名称/* parsed from URL */AVal playpath0;     //从URL解析出来的playpath/* passed in explicitly */AVal playpath;      //显式指定的playpathAVal tcUrl;         //要连接的目标流的URL。默认值为:rtmp[e]://host[:port]/app/playpath ,由解析出的各个字段值拼接而成。AVal swfUrl;        //媒体的SWF播放器的URL,默认不设置任何值AVal pageUrl;       //嵌入网页的媒体的URL,默认不设置任何值AVal app;           //要连接的服务器上的应用程序AVal auth;          //验证字符串,它会追加到Connect Message的末尾。使用这个选项,//实际上将会追加一个布尔值TRUE然后才是这个验证字符串。只有某些特殊的服务器需要,而且已经被废弃。AVal flashVer;      //用于运行SWF播放器的Flash插件的版本。默认为“LUX 10,0,32,18"AVal subscribepath; //要访问的流的名称AVal token;         //SecureToken Response中要使用的key。当服务器需要一个SecureToken验证时使用AMFObject extras;int edepth;int seekTime;       //访问媒体时的偏移时间int stopTime;       //访问媒体的停止时间#define RTMP_LF_AUTH  0x0001  /* using auth param */                  //是否使用验证参数
#define RTMP_LF_LIVE    0x0002  /* stream is live */                    //请求的流是否为实时流
#define RTMP_LF_SWFV    0x0004  /* do SWF verification */               //是否要执行SWF验证
#define RTMP_LF_PLST    0x0008  /* send playlist before play */         //是否在发送play之间发送playlist
#define RTMP_LF_BUFX    0x0010  /* toggle stream on BufferEmpty msg */  //是否在BufferEmpty msg时toggle stream
#define RTMP_LF_FTCU    0x0020  /* free tcUrl on close */               //close时是否释放tcUrlint lFlags;         //复合标识int swfAge;         //指定缓存的SWF信息的有效天数,超过这个天数后,将重新检查int protocol;       //服务器的rtmp协议类型/* connection timeout in seconds */int timeout;       //连接timeout的时间值unsigned short socksport;       //代理主机的端口unsigned short port;            //服务器的端口//加密相关的字段
#ifdef CRYPTO
#define RTMP_SWF_HASHLEN    32void *dh;         /* for encryption */void *rc4keyIn;void *rc4keyOut;uint32_t SWFSize;uint8_t SWFHash[RTMP_SWF_HASHLEN];char SWFVerificationResponse[RTMP_SWF_HASHLEN+10];
#endif
} RTMP_LNK;//RTMP业务层,即建立rtmp流之后对rtmp流做必要操作所需的参数
//必要操作如:seek操作,resume操作
/* state for read() wrapper */
typedef struct RTMP_READ
{char *buf;                      //指向读取缓冲区char *bufpos;                   //指向未处理数据的指针unsigned int buflen;            //未处理数据的大小uint32_t timestamp;             //RTMP流的当前时间戳uint8_t dataType;               //RTMP流的数据类型,即是否包含音频数据和视频数据 0x04为音频 0x01为视频,使用的是flv的表示法uint8_t flags;                  //解析flag,包含以下几个值
#define RTMP_READ_HEADER    0x01        //表示是否在当前rtmp流的开头中插入flv header,默认不会设置这个状态,实际开始读取时插入对应的flv header,然后设置它,表示已添加flv header
#define RTMP_READ_RESUME    0x02        //表示是否要进行resume
#define RTMP_READ_NO_IGNORE 0x04        //
#define RTMP_READ_GOTKF     0x08        //表示是否完成了resume
#define RTMP_READ_GOTFLVK   0x10        //
#define RTMP_READ_SEEKING   0x20        //表示是否要执行seek操作int8_t status;                  //读取的当前状态,表示当前的流的分析结果,为以下四个取值,为0表示正常
#define RTMP_READ_COMPLETE  -3          //Close或者Stop状态,表示读取完成
#define RTMP_READ_ERROR -2              //致命错误
#define RTMP_READ_EOF   -1              //EOF
#define RTMP_READ_IGNORE    0           //可忽略错误/* if bResume == TRUE */        //resume时需要指定的字段,用于帮助流定义resume的位置uint8_t initialFrameType;           //定位的帧的类型,即是视频帧还是音频帧uint32_t nResumeTS;                 //定位的帧的时间戳char *metaHeader;                   //要resume的流的metedata数据char *initialFrame;                 //定位的帧的datauint32_t nMetaHeaderSize;           //要resume的流的metadata数据的尺寸uint32_t nInitialFrameSize;         //定位的帧的data lengthuint32_t nIgnoredFrameCounter;uint32_t nIgnoredFlvFrameCounter;
} RTMP_READ;//method:方法
//表示RTMP的方法或函数
typedef struct RTMP_METHOD
{AVal name;int num;
} RTMP_METHOD;//表示一个RTMP流,用于保存这个RTMP流的相关参数
typedef struct RTMP
{int m_inChunkSize;          //接收方向的max chunk sizeint m_outChunkSize;int m_nBWCheckCounter;int m_nBytesIn;             //接受到的字节的总数量int m_nBytesInSent;         //发送的字节的总数量int m_nBufferMS;int m_stream_id;      /* returned in _result from createStream */ //Message Stream IDint m_mediaChannel;         //当前media使用的chunk Stream iduint32_t m_mediaStamp;      //当前media的时间戳uint32_t m_pauseStamp;int m_pausing;int m_nServerBW;            //window sizeint m_nClientBW;            //Set Peer Bandwidth Message中的window sizeuint8_t m_nClientBW2;       //Set Peer Bandwidth Message中的limit typeuint8_t m_bPlaying;         //当前是否playuint8_t m_bSendEncoding;uint8_t m_bSendCounter;int m_numInvokes;           //记录RTMP发起的invoke的数量int m_numCalls;             //m_methodCalls中的数量/* remote method calls queue */RTMP_METHOD *m_methodCalls; //request列表RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];     //输入的chunk Stream对应的最后一个packetRTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];    //输出的chunk stream对应的最后一个packet/* abs timestamp of last packet */int m_channelTimestamp[RTMP_CHANNELS];          //最后一个packet对应的绝对时间戳/* audioCodecs for the connect packet */double m_fAudioCodecs;                      //Connect msg中的音频编解码/* videoCodecs for the connect packet */double m_fVideoCodecs;                      //Connect msg中的视频编解码/* AMF0 or AMF3 */double m_fEncoding;                         //使用AMF0还是AMF3/* duration of stream in seconds */double m_fDuration;                         //当前流的长度,单位秒/* RTMPT stuff */       //以下是RTMPT相关的成员变量:int m_msgCounter;       //int m_polling;          //int m_resplen;          //HTTP Response的body长度,表示是否读取到了HTTP Response,但数据保存在SB中int m_unackd;           //表示未应答的HTTP request数量(一般,没一个request必有一个response)AVal m_clientID;        //底层为HTTP时,用于表示客户端IDRTMP_READ m_read;       //RTMP层面的读RTMPPacket m_write;     //RTMP层面write时的临时msg对象RTMPSockBuf m_sb;       //套接字层面的读写RTMP_LNK Link;          //RTMP连接的参数
} RTMP;//解析RTMP地址
int RTMP_ParseURL(const char *url, int *protocol, AVal *host,unsigned int *port, AVal *playpath, AVal *app);
//从RTMP URL中解析playpath
void RTMP_ParsePlaypath(AVal *in, AVal *out);
//设置缓冲的时长,单位:毫秒
void RTMP_SetBufferMS(RTMP *r, int size);
//发送“设置缓冲时长”的控制包
void RTMP_UpdateBufferMS(RTMP *r);//设置RTMP的相关属性
int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg);//设置url
int RTMP_SetupURL(RTMP *r, char *url);//设置流
void RTMP_SetupStream(RTMP *r, int protocol,  //协议AVal *hostname,                     //hostnameunsigned int port,                  //portAVal *sockshost,AVal *playpath,AVal *tcUrl,AVal *swfUrl,AVal *pageUrl,AVal *app,AVal *auth,AVal *swfSHA256Hash,uint32_t swfSize,AVal *flashVer,AVal *subscribepath,int dStart,int dStop, int bLiveStream, long int timeout);//客户端与服务器建立连接,并进行Connect
int RTMP_Connect(RTMP *r, RTMPPacket *cp);
struct sockaddr;
int RTMP_Connect0(RTMP *r, struct sockaddr *svc);
int RTMP_Connect1(RTMP *r, RTMPPacket *cp);//服务端处理handshake,成功返回值大于0
int RTMP_Serve(RTMP *r);//RTMP读取到一个packet,并不保证一定读取到完整的packet
int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
//使用RTMP发送packet,queue表示是否按照队列发送
int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);
//RTMP发送一个chunk
int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk);
//RTMP是否处于连接状态
int RTMP_IsConnected(RTMP *r);
//返回RTMP关联的套接字
int RTMP_Socket(RTMP *r);
//RTMP是否中断
int RTMP_IsTimedout(RTMP *r);
//获得RTMP当前流的长度
double RTMP_GetDuration(RTMP *r);//暂停或者恢复流
int RTMP_ToggleStream(RTMP *r);//连接到url指定的流,返回TRUE和FLASE
int RTMP_ConnectStream(RTMP *r, int seekTime);
//重新连接流,即delete Stream之后再次Connect Stream,且同步等待到流开始
int RTMP_ReconnectStream(RTMP *r, int seekTime);
//停止流,或者删除流
void RTMP_DeleteStream(RTMP *r);//获取一个媒体packet,即音频帧或者视频帧(在RTMP_ReadPacket函数之上实现)
int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet);//客户端处理packet
int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet);//初始化RTMP,将RTMP各个字段设置为默认值
void RTMP_Init(RTMP *r);
//关闭这个RTMP,如果当前处于连接状态,则会关闭连接;然后释放RTMP相关的内容
void RTMP_Close(RTMP *r);
//分配一个RTMP
//其操作仅仅是分配一块RTMP大小的内存而已,RTMP也可以在栈上分配
RTMP *RTMP_Alloc(void);
//释放这块RTMP大小的内存
void RTMP_Free(RTMP *r);//为RTMP Link添加写属性
void RTMP_EnableWrite(RTMP *r);
//返回RTMP库的版本号
int RTMP_LibVersion(void);
//RTMP流程被打断
//本质为设置全局变量RTMP_ctrlC为true
void RTMP_UserInterrupt(void);  /* user typed Ctrl-C */int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject,unsigned int nTime);/* caller probably doesn't know current timestamp, should* just use RTMP_Pause instead*/
int RTMP_SendPause(RTMP *r, int DoPause, int dTime);
int RTMP_Pause(RTMP *r, int DoPause);int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,AMFObjectProperty * p);//TCP层面的操作:读、写、关闭
//在TCP层面读取,返回读取到的字节数
int RTMPSockBuf_Fill(RTMPSockBuf *sb);
//在TCP层面写入,返回写入的返回值
int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len);
//关闭套接字,返回关闭的返回值
int RTMPSockBuf_Close(RTMPSockBuf *sb);//
int RTMP_SendCreateStream(RTMP *r);
int RTMP_SendSeek(RTMP *r, int dTime);
int RTMP_SendServerBW(RTMP *r);
int RTMP_SendClientBW(RTMP *r);
//丢弃第i个request
//freeit表示是否释放这个request
void RTMP_DropRequest(RTMP *r, int i, int freeit);//FLV层面
int RTMP_Read(RTMP *r, char *buf, int size);
int RTMP_Write(RTMP *r, const char *buf, int size);/* hashswf.c */
int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,int age);#ifdef __cplusplus
};
#endif#endif

librtmp库API介绍及其结构概述相关推荐

  1. 使用librtmp库进行推流与拉流

    使用librtmp库进行推流与拉流 目前比较主流的直播技术有RTMP.HLS,其中RTMP主要基于TCP协议,HLS主要基于HTTP协议,二者在实施成本.延迟性等方面有较大差异.本文主要讲解RTMP的 ...

  2. jquery 字符串查找_Python Appium 库IOS特有元素查找API介绍

    IOS自动化测试系列文章: 一文带你趟过mac搭建appium测试环境的遇到的坑 appium 实现简易IOS  app自动化测试demo 今天分享的是python appium库中IOS元素查找特定 ...

  3. c实现的Flickr API库Flickcurl介绍

    api介绍 http://www.flickr.com/services/api/ Flickcurl: C library for the Flickr API http://librdf.org/ ...

  4. C++各大有名库的介绍之C++标准库

    C++各大有名库的介绍之C++标准库 标准库中提供了C++程序的基本设施.虽然C++标准库随着C++标准折腾了许多年,直到标准的出台才正式定型,但是在标准库的实现上却很令人欣慰得看到多种实现,并且已被 ...

  5. ORTP库API使用入门

    转自:https://www.cnblogs.com/elisha-blogs/p/3963126.html ORTP库API使用入门 一.简介 ORTP是一个支持RTP以及RFC3550协议的库,有 ...

  6. C++各大有名库的介绍(一)

    C++各大有名库的介绍之C++标准库 标准库中提供了C++程序的基本设施.虽然C++标准库随着C++标准折腾了许多年,直到标准的出台才正式定型,但是在标准库的实现上却很令人欣慰得看到多种实现,并且已被 ...

  7. 【STM32H7教程】第29章 STM32H7的USART串口基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第29章       STM32H7的USART串口基础知识和 ...

  8. 【STM32H7教程】第87章 STM32H7的SDMMC总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第87章       STM32H7的SDMMC总线基础知识和 ...

  9. 【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第72章       STM32H7的SPI总线基础知识和HA ...

  10. 【STM32H7教程】第91章 STM32H7的FDCAN总线基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第91章       STM32H7的FDCAN总线基础知识和 ...

最新文章

  1. python策略模式_设计模式(python实现):策略模式
  2. 华为云计算玉溪总经理_华为云计算(6)——FusionAccess
  3. android自定义弹出对话框,使用FlyDialog实现自定义Android弹窗对话框
  4. 记录一下代码编辑器的主题修改
  5. Spark structured 记录一次kudu扩容导致无法写入数据的问题
  6. 在matlab环境中实现图像的傅里叶变换,matlab用傅里叶变换实现图像的低通滤波
  7. 机器学习之深入理解K-means、与KNN算法区别及其代码实现
  8. 前端实现序列帧_Html5 序列帧动画
  9. “流动书库”藏书汇总,欢迎借阅
  10. FastDFS自定义文件存储系统
  11. 漫谈云数据中心的前世今生
  12. 关于element-ui的blur事件失效,select的blur的bug,以及row在@blur延迟的解决
  13. 动手的乐趣_1969功率放大器
  14. MZOJ #82 总统竞选
  15. 云IDE:Coding的Cloud Studio
  16. STM32-点亮一盏灯
  17. http请求过程(访问一个页面,发生了怎样的网络请求?)
  18. 爱分析CCFA联合发布《2021中国购物中心数字化趋势报告》
  19. windows运行命令安全隐患
  20. 1.C语言入门知识(codeblocks)

热门文章

  1. db2与mysql语法区别_db2和mysql语法的区别是什么
  2. 爬虫python代码网易云_用python爬取网易云音乐歌曲的歌词
  3. java servlet试题_JAVA servlet 面试题
  4. CROW-5 WEB APP引擎商业计划书(HTML5方向)-微信网页版微信公众平台登录-水仙谷...
  5. 台式计算机可以发射无线网络,台式电脑无线网卡怎么发射WIFI信号!
  6. python进阶路线 知乎_Python自学路线图之Python进阶
  7. kindle资源网址
  8. python爬虫之利用pyquery爬取当当网图书信息
  9. labview变量和C语言,LabVIEW
  10. Podfile文件用法详解