前面深入理解rtmp(1)之开发环境搭建中我们已经搭建好服务器,并且利用一些现成的工具可以推送直播流,播放直播流了.这篇文章我们开始搭建从零开发一套rtmp推流拉流sdk,对着协议实现,达到真正的"深入理解".
作为一个码农,搬砖搬到一定高度就需要"脚手架"来支撑我们"够得住".为了方面我们把rtmp推拉流sdk实现为一个PC上的命令行程序,当开发调试稳定后,我们可以快速的通过交叉编译工具编译到Android/iOS等移动端设备.
1.创建工程
我们使用cmake作为安装编译工具,需要安装cmake,mac下执行brew install cmake
. 在我们的rtmpsdk路径下创建CMakeLists.txt:
//指定cmake最低版本
cmake_minimum_required (VERSION 3.6)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE PATH "Installation directory" FORCE)
message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC -ffunction-sections -fdata-sections -Os")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -ffunction-sections -fdata-sections -Os")
project (rtmpsdk)
set(SRC_PREFIX "src")
set(SELF_LIBS_OUT ${CMAKE_SYSTEM_NAME}.out)
file(GLOB SELF_SRC_FILES
${SRC_PREFIX}/main.cpp
)
add_executable(${PROJECT_NAME} ${SELF_SRC_FILES})
创建src目录,创建main.cpp文件:
#include <iostream>
int main(int argc,char* argv[])
{
//标准输出到控制台
std::cout << "Hello rtmp server!" << std::endl;
return 0;
}
在rtmpsdk下创建cmake_build文件夹作为我们的输出路径 在控制台,我们进入我们的工程路径后执行:
cd cmake_build
然后执行:
cmake ..
make
在camke下面生成了编译中间文件和最终的rtmpsdk文件:
现在执行一下./rtmpsdk
:
$ ./rtmpsdk
Hello rtmp server!
可以看到我们打印的"Hello rtmp server!",编译环境已经搭建好了,可以继续往下实现我们的功能了.
注:我的开发环境是mac,windows环境后面我提供一个docker的centos镜像作为我们工程的编译环境.
2.封装接口
我们想象一下,我们的rtmp应该对外提供什么接口?封装什么数据结构?
- 我们要连接我们的服务器,rtmp是基于tcp,那么我们要创建一个socket网络套接字,那么我们需要一个根据url创建对象的接口
rtmp_t rtmp_create(const char* url)
创建socket后我们还需要做一些配置,最基本的我们要配置读写超时时间,如果我们的socket没有超时,我们的读写函数一直没有返回,会导致无法退出的问题,所以我们需要提供一个设置读写超时的接口:int rtmp_set_timeout(rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms)
rtmp有握手过程,接下来需要一个握手接口:int rtmp_handshake(rtmp_t rtmp)
握手成功后开始连接服务器,提供连接接口:int rtmp_connect_app(rtmp_t rtmp)
连接成功后通知服务器是拉流还是推流,提供两个函数:int rtmp_play_stream(rtmp_t rtmp)
,int rtmp_publish_stream(rtmp_t rtmp)
可以开始拉流或推流了:int rtmp_read_packet(rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size)
,int rtmp_write_packet(rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size)
拉推流结束后,销毁对象释放资源:void rtmp_destroy(rtmp_t rtmp)
以播放为例用一个图表示:
接口定义好了,我们在src下新建libs目录,创建我们对外暴露的rtmpsdk.hpp文件:
#ifndef LIB_RTMP_HPP
#define LIB_RTMP_HPP
/**
* rtmpsdk is a librtmp like library,
* used to play/publish rtmp stream from/to rtmp server.
* socket: use sync and block socket to connect/recv/send data with server.
* depends: no need other libraries; depends on ssl if use complex_handshake.
* thread-safe: no
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C"{
#endif
/*************************************************************
**************************************************************
* RTMP protocol context
**************************************************************
*************************************************************/
// the RTMP handler.
typedef void* rtmp_t;
/**
* Create a RTMP handler.
* @param url The RTMP url, for example, rtmp://localhost/live/livestream
* @remark default timeout to 30s if not set by rtmp_set_timeout.
* @remark default schema to url_schema_normal, use rtmp_set_schema to change it.
*
* @return a rtmp handler, or NULL if error occured.
*/
extern rtmp_t rtmp_create(const char* url);
/**
* set socket timeout
* @param recv_timeout_ms the timeout for receiving messages in ms.
* @param send_timeout_ms the timeout for sending message in ms.
* @remark user can set timeout once rtmp_create,
* or before rtmp_handshake or rtmp_dns_resolve to connect to server.
* @remark default timeout to 30s if not set by rtmp_set_timeout.
*
* @return 0, success; otherswise, failed.
*/
extern int rtmp_set_timeout(rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms);
/**
* close and destroy the rtmp stack.
* @remark, user should never use the rtmp again.
*/
extern void rtmp_destroy(rtmp_t rtmp);
/*************************************************************
**************************************************************
* RTMP protocol stack
**************************************************************
*************************************************************/
/**
* connect and handshake with server
* category: publish/play
* previous: rtmp-create
* next: connect-app