之前,在百度空间中写了几篇博客,现在准备将博客迁移到CSDN上。博客原文如下:
在使用librtmp库来开发rtmp客户端程序;而熟悉red5的童鞋都知道,red5是支持客户端以record方式向其推送直播流,实现一边直播,一边录制文件。但是,librtmp库偏偏只支持以live方式向red5推送直播流。没办法,只好想办法解决。但是,网上也没找到其他网友分享的解决方案。看来,只好自己来解决了。
其实,解决这个问题,也不是很难。毕竟,代码面前,了无秘密。
关键是,了解rtmp协议,以及librtmp源代码。
1.rtmp协议关于推送命令:publish命令,该命令设置了推送的方式:live方式,record方式和append方式。该命令具体说明如下,摘自:Adobe 的实时消息协议(rtmp)(中文版)。排版不好,关键是红色文件部分。
客户端向服务器发送publish 命令用来发布带有名字的流。任何客户端都可以使用这个
名字播放这个流和接收发布的音频,视频和数据信息。
从客户端发往服务器的这个命令结构如下:
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | 命令的名字,设置为“publish”。|
+--------------+----------+----------------------------------------+
|Transaction ID| Number | 事务ID 为0 |
+--------------+----------+----------------------------------------+
| Command | Null | 命令信息不存在,设置为空。|
| Object | | |
+--------------+----------+----------------------------------------+
| Publishing | String | 发布的流的名称。|
| Name | | |
+--------------+----------+----------------------------------------+
| Publishing | String | 发布的类型。设置为“live”、“record” |
| Type | | 或“append”。|
| | | |
| | | record:发布流并且数据被录制在一个新文|
| | | 件中。这个文件被存储在服务器应用程序的|
| | | 子目录中。如果文件已经存在,它将会被|
| | | 覆盖。|
| | | |
| | | append:发布流并且数据被追加在文件的后|
| | | 面,如果文件不存在,它将会被创建。|
| | | |
| | | live:发布直播流,数据不会被录制。|
+--------------+----------+----------------------------------------+
服务器使用onStatus 命令回复并标记了发布的起点。
名字播放这个流和接收发布的音频,视频和数据信息。
从客户端发往服务器的这个命令结构如下:
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | 命令的名字,设置为“publish”。|
+--------------+----------+----------------------------------------+
|Transaction ID| Number | 事务ID 为0 |
+--------------+----------+----------------------------------------+
| Command | Null | 命令信息不存在,设置为空。|
| Object | | |
+--------------+----------+----------------------------------------+
| Publishing | String | 发布的流的名称。|
| Name | | |
+--------------+----------+----------------------------------------+
| Publishing | String | 发布的类型。设置为“live”、“record” |
| Type | | 或“append”。|
| | | |
| | | record:发布流并且数据被录制在一个新文|
| | | 件中。这个文件被存储在服务器应用程序的|
| | | 子目录中。如果文件已经存在,它将会被|
| | | 覆盖。|
| | | |
| | | append:发布流并且数据被追加在文件的后|
| | | 面,如果文件不存在,它将会被创建。|
| | | |
| | | live:发布直播流,数据不会被录制。|
+--------------+----------+----------------------------------------+
服务器使用onStatus 命令回复并标记了发布的起点。
2.修改librtmp代码
librtmp关于发送publish命令,在源文件rtmp.c的函数static int SendPublish(RTMP *r)中。源码如下:
librtmp关于发送publish命令,在源文件rtmp.c的函数static int SendPublish(RTMP *r)中。源码如下:
SAVC(publish);//publish命令
SAVC(live);//live参数
SAVC(record);//record参数
static int
SendPublish(RTMP *r)
{
RTMPPacket packet;
char pbuf[1024], *pend = pbuf + sizeof(pbuf);
char *enc;
packet.m_nChannel = 0x04; /* source channel (invoke) */
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = r->m_stream_id;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_publish);
enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
if (!enc)
return FALSE;
/* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
enc = AMF_EncodeString(enc, pend, &av_live);//设置live参数
if (!enc)
return FALSE;
packet.m_nBodySize = enc - packet.m_body;
return RTMP_SendPacket(r, &packet, TRUE);
}
我们可以看到,源码中都已经设置了record参数;但是,作者没有使用,不知为什么?因此,我们只要做一下简单的修改就可以了。
SAVC(publish);
SAVC(live);
SAVC(record);
SAVC(append);
static int
SendPublish(RTMP *r)
{
RTMPPacket packet;
char pbuf[1024], *pend = pbuf + sizeof(pbuf);
char *enc;
packet.m_nChannel = 0x04; /* source channel (invoke) */
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = r->m_stream_id;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_publish);
enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
if (!enc)
return FALSE;
/* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
//根据不同的参数来设置不同的推送方式
if (r->Link.lFlags & RTMP_LF_PUBLISH_LIVE)
{
enc = AMF_EncodeString(enc, pend, &av_live);
}
else if (r->Link.lFlags & RTMP_LF_PUBLISH_RECORD)
{
enc = AMF_EncodeString(enc, pend, &av_record);
}
else if (r->Link.lFlags & RTMP_LF_PUBLISH_APPEND)
{
enc = AMF_EncodeString(enc, pend, &av_append);
}
else
{
enc = AMF_EncodeString(enc, pend, &av_live);
}
if (!enc)
return FALSE;
packet.m_nBodySize = enc - packet.m_body;
return RTMP_SendPacket(r, &packet, TRUE);
}
其中,自己增加的宏如下,在头文件rtmp.h中:
define RTMP_LF_AUTH 0x0001 /* using auth param */
define RTMP_LF_LIVE 0x0002 /* stream is live */
define RTMP_LF_SWFV 0x0004 /* do SWF verification */
define RTMP_LF_PLST 0x0008 /* send playlist before play */
define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */
define RTMP_LF_PUBLISH_LIVE 0x0040 /* publish in LIVE way */
define RTMP_LF_PUBLISH_RECORD 0x0080 /* publish in RECORD way */
define RTMP_LF_PUBLISH_APPEND 0x0100 /* publish in APPEND way */
使用时,示例代码如下:
if(nPublishType == RTMP_PUBLISH_LIVE)
{
pRTMP->Link.lFlags |= RTMP_LF_PUBLISH_LIVE;
}
else if(nPublishType == RTMP_PUBLISH_RECORD)
{
pRTMP->Link.lFlags |= RTMP_LF_PUBLISH_RECORD;
}
else if(nPublishType == RTMP_PUBLISH_APPEND)
{
pRTMP->Link.lFlags |= RTMP_LF_PUBLISH_APPEND;
}
else
{
pRTMP->Link.lFlags |= RTMP_LF_PUBLISH_LIVE;
}
本文介绍如何通过修改librtmp库源代码实现不同推送模式,包括live直播、record录制及append追加,以便向Red5服务器推送直播流的同时进行文件录制。

2707

被折叠的 条评论
为什么被折叠?



