IOS 下实现一对一直播平台系统 libRtmp 推拉流

2019-03-18 11:06:24 浏览数 (1)

前言

今天向大家介绍一下如何使用 librtmp 库推拉音视频直播流。虽然Adobe已经宣布不在维护 flash 了,但 flash使用的 rtmp 协议目前仍然是直播系统中最常用的传输协议。

目前实现 rtmp 协议的有很多库,但在众多 rtmp 协议的实现中,librtmp 无疑是最好的一个。

今天我们就向大家介绍一下,如何在 iOS 中使用 librtmp。

交叉编译 librtmp

在开发移动端程序时,我们都会用到交叉编译。那么什么是交叉编译呢?交叉编译就是指在一种 CPU类型/操作系统 的机子上编译出另一种 CPU类型/操作系统 的机子上可以运行的程序就称为交叉编译。

以 iOS 为例,我们在 Mac 环境上(CPU类型为 Intel)编译出 iPhone 手机(CPU类型为 ARM)可执行的程序。这个工作我们几乎每天都在做,只是大家没有观注而已。

同样,我们想在 iOS 程序中使用 librtmp 库,就必须先将 librtmp 编译成 ARM 架构 的可执行的程序(也就是 iPhone 手机可执行的程序),这样才能在 iOS 程序中使用它。

Xcode 为我们提供了交叉编译的工具。可以通过 xcode-select --print-path 可获取交叉编译工具目录,在该目录的 user/bin 下就可以找到交叉编译的工具。最主要的两个工具是 gcc 编译器和 ld 链接器。

下面我们来详细介绍一下 gcc 及 ld 参数的含义:

  • isysroot: 指定目标机SDK位置,也就是iPhone手机开发SDK的位置。
  • arch: 指定目标机 CPU 架构。
  • miphoneos-version-min: 指定可用的操作系统最小版本。
  • fembed-bitcode: 苹果自带的参数。从 iOS7.0 开始支持。有兴趣的同学在线上查一上资料吧。

下面是编译 librtmp 的部分关键脚本:

代码语言:javascript复制
DEVELOPER=$(xcode-select --print-path)

SDK_VERSION=$(xcrun -sdk iphoneos --show-sdk-version)
echo $SDK_VERSION
SDK_VERSION_MIN=7.0

DEVICE_PLATFORM="${DEVELOPER}/Platforms/iPhoneOS.platform"
SIMULATOR_PLATFORM="${DEVELOPER}/Platforms/iPhoneSimulator.platform"
DEVICE_SDK="${DEVICE_PLATFORM}/Developer/SDKs/iPhoneOS${SDK_VERSION}.sdk"
SIMULATOR_SDK="${SIMULATOR_PLATFORM}/Developer/SDKs/iPhoneSimulator${SDK_VERSION}.sdk"

...

CROSS_COMPILE="${DEVELOPER}/usr/bin/" 
XCFLAGS="-g -O2 -isysroot ${SDK} -I${IOS_OPENSSL}/include -arch $ARCH -miphoneos-version-min=${SDK_VERSION_MIN} -fembed-bitcode" 
XLDFLAGS="-isysroot ${SDK} -arch $ARCH -L${IOS_OPENSSL}/lib -lssl" 
make SYS=darwin

make SYS=darwin prefix="/tmp/librtmp-$ARCH" install &> "/tmp/librtmp-$ARCH.log"

执行该脚本后,它就指定好了做交叉编译的编译器及链接器。并指定好了编译参数和链接参数。最后,Makefile 会使用指定的编译器对代码进行编译,生成可以在iPhone上运行的程序。

使用 librtmp 拉流

使用 librtmp 接流的基本步骤:

  1. 分配 RTMP 对象,并初始化 RTMP *rtmp=RTMP_Alloc(); RTMP_Init(rtmp);
  2. 设置 rtmp URL RTMP_SetupURL(rtmp,"rtmp://live.hkstv.hk.lxdns.com/live/hks")
  3. 设置缓冲区大小 //1hour RTMP_SetBufferMS(rtmp, 3600*1000);
  4. 连接流媒体服务器 ... RTMP_Connect(rtmp,NULL) ... RTMP_ConnectStream(rtmp,0) ...
  5. 拉流 while((nRead=RTMP_Read(rtmp,buf,bufsize)) > 0 { ... }
  6. 关闭连接 RTMP_Close(rtmp); RTMP_Free(rtmp);

librtmp 推流

使用 librtmp 推流与拉流的流程差不多。刚开始时也是分配并初始化 RTMP, 只是在连接流媒体服务器时需要打开写开关。代码如下:

代码语言:javascript复制
//if unable,the AMF command would be 'play' instead of 'publish'
RTMP_EnableWrite(rtmp);

if (!RTMP_Connect(rtmp,NULL)){
    ...
}

if (!RTMP_ConnectStream(rtmp,0)){
    ...
}

连接到流媒体服务器后,设置并初始化 RTMPPacket,RTMPPacket 里边存放要发送的数据包。代码如下:

代码语言:javascript复制
packet=(RTMPPacket*)malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet,1024*64);
RTMPPacket_Reset(packet);

packet->m_hasAbsTimestamp = 0;
packet->m_nChannel = 0x04;
packet->m_nInfoField2 = rtmp->m_stream_id;

下面开始将 FLV 文件推到流媒体服务器上。首先,跳过 FLV 头。

代码语言:javascript复制
//jump over FLV Header
fseek(fp,9,SEEK_SET);

FLV 文件格式是由 FLV Header FLV Body组成的。其中 FLV Header一共 9 个字节。FLV Body 由 Tag Header Tag Body 组成。

然后,在一个 while 循环中不停的分析Tag,从 Tag Header 和 Tag Body中取出数据,构造好 RTMPPacket 后 push 到流媒体服务器。

代码语言:javascript复制
...

if(fread(packet->m_body,1,datalength,fp)!=datalength)
    break;

packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
packet->m_nTimeStamp = timestamp;
packet->m_packetType = type;
packet->m_nBodySize  = datalength;
pre_frame_time=timestamp;

if (!RTMP_IsConnected(rtmp)){
  ...
}
if (!RTMP_SendPacket(rtmp,packet,0)){
  ...
}

...

最后,将 flv 文件全部推送到媒体服务器后,关闭 rtmp 连接。

小结

本文首先介绍了如何在 Mac 上编译出可以在 iOS 上可运行的 librtmp 库,然后介绍了如何从流媒体服务器上拉流,最后介绍了如何使用 librtmp 将 flv 文件推送到流媒体服务器上。

0 人点赞