将h.264裸码流推送到RTMP服务器

h.264裸码流的格式,参考“H.264-AVC-ISO_IEC_14496-10.pdf, page 211.”,这个文档的下载地址:https://github.com/winlinvip/simple-rtmp-server/tree/develop/trunk/doc/H.264-AVC-ISO_IEC_14496-10.pdf

一个录制的h.264裸码流文件:http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw,或者:http://ossrs.net/srs.release/3rdparty/720p.h264.raw,里面的格式是annexb格式:

// SPS
000000016742802995A014016E40
// PPS
0000000168CE3880
// IFrame
0000000165B8041014C038008B0D0D3A071.....
// PFrame
0000000141E02041F8CDDC562BBDEFAD2F.....

一般可以从IP摄像头的sdk中拿到这种数据,一般sdk会使用如下接口:

int device_read(char** pdata, int* psize, int* pdts, int* ppts);

也有的摄像头没有B帧,所以dts和pts是一致的,所以后面的时间戳合并成一个:

int device_read(char** pdata, int* psize, int* timestamp);

或者,使用回调函数,当摄像头编码出h.264数据时回调这个函数,格式和上面的也差不多。

本文描述了如何将拿到的h.264数据,通过RTMP协议发布到RTMP服务器,然后使用RTMP或者HLS播放。

srs-librtmp

显然发送h.264的数据得使用RTMP库,rtmpdump提供的librtmp要求是flv/RTMP格式的数据,而srs-librtmp提供了接口直接发送h.264数据。

srs-librtmp是SRS服务器提供的客户端库,SRS项目参考:https://github.com/winlinvip/simple-rtmp-server

srs-librtmp的wiki参考:https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_SrsLibrtmp#publish-h264-raw-data

对应的srs的bug参考:https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521

使用git clone下来,可以选择github源,或者国内的其他镜像源,参考:https://github.com/winlinvip/simple-rtmp-server#mirrors

例如:

git clone https://github.com/winlinvip/simple-rtmp-server.git

SRS目前有两个分支,只有SRS2(即master分支)提供了h.264裸码流发送的功能,git clone之后要切换到这个分支:

cd simple-rtmp-server/trunk
git checkout master

如果你可以使用.h和.a库,就可以直接编译srs,可以看到生成了.h和.a文件:

./configure --disable-all --with-librtmp && make && 
ls -lh objs/include/srs_librtmp.h objs/lib/srs_librtmp.a 

如果是需要将srs-librtmp导出成一个.h和.cpp文件,执行下面的命令,可以看到生成了.h和.cpp文件:

./configure --export-librtmp-single=objs/srs-librtmp && 
ls -lh objs/srs-librtmp/srs_librtmp.h objs/srs-librtmp/srs_librtmp.cpp 


下面就可以编写程序,读取h.264裸码流,然后调用srs-librtmp发送出去了。

srs_h264_raw_publish

SRS提供了例子读取录制的h.264文件并发布到RTMP服务器:https://github.com/winlinvip/simple-rtmp-server/tree/develop/trunk/research/librtmp/srs_h264_raw_publish.c

录制的h.264裸码流文件:http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw,或者:http://ossrs.net/srs.release/3rdparty/720p.h264.raw,里面的格式是annexb格式:

// SPS
000000016742802995A014016E40
// PPS
0000000168CE3880
// IFrame
0000000165B8041014C038008B0D0D3A071.....
// PFrame
0000000141E02041F8CDDC562BBDEFAD2F.....


下载这个h.264裸码流文件和实例文件:

wget https://raw.githubusercontent.com/winlinvip/simple-rtmp-server/master/trunk/research/librtmp/srs_h264_raw_publish.c -O objs/srs-librtmp/srs_h264_raw_publish.c --no-check-certificate &&
wget http://ossrs.net/srs.release/3rdparty/720p.h264.raw -O objs/srs-librtmp/720p.h264.raw

查看srs-librtmp目录,应该是下面的结构:

[winlin@dev6 trunk]$ ls -lh  objs/srs-librtmp
total 5.8M
-rw-rw-r-- 1 winlin winlin 5.1M Nov  8 12:39 720p.h264.raw
-rw-rw-r-- 1 winlin winlin  633 Nov 16 10:32 example.c
-rw-rw-r-- 1 winlin winlin  87K Nov 16 10:36 srs_h264_raw_publish.c
-rw-rw-r-- 1 winlin winlin 557K Nov 16 10:32 srs_librtmp.cpp
-rw-rw-r-- 1 winlin winlin  20K Nov 16 10:32 srs_librtmp.h

srs_h264_raw_publish.c读取h.264裸码流后,基本上读取到的就是一个一个的h.264 annexb格式的包,参考read_h264_frame():

// SPS
000000016742802995A014016E40
// PPS
0000000168CE3880
// IFrame
0000000165B8041014C038008B0D0D3A071.....
// PFrame
0000000141E02041F8CDDC562BBDEFAD2F.....


连接RTMP和发送的主要函数是:

srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url);
srs_simple_handshake(rtmp);
srs_connect_app("rtmp://127.0.0.1/live/livestream");
srs_publish_stream(rtmp);
while (!EOF) {
    read_h264_frame(&data, &size, &dts, &pts);
    srs_h264_write_raw_frames(rtmp, data, size, dts, pts);
}

这几个函数就可以把h.264裸码流发出去了。

编译和运行

若使用srs-librtmp导出的单个.h和.cpp文件,编译和运行命令是:

cd objs/srs-librtmp &&
gcc -g -O0 srs_h264_raw_publish.c srs_librtmp.cpp -o publisher -lstdc++ &&
./publisher ./720p.h264.raw rtmp://ossrs.net/live/h264.raw

播放的RTMP地址为:rtmp://ossrs.net/live/h264.raw,打开下面的链接即可观看流:

http://www.ossrs.net/srs.release/trunk/research/players/srs_player.html?server=ossrs.net&vhost=ossrs.net&stream=h264.raw&autostart=true

接口

srs-librtmp定义的h.264裸码流发送接口使用参考:https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521

其中有几个错误可以忽略:

  • srs_h264_is_dvbsp_error:这个是因为IP摄像头在每个I帧前都插入了SPS和PPS,所以在服务器断开重连时,重新调用srslibrtmp的连接和publish函数,不用考虑接下来的帧是否是sps和pps。但是RTMP要求第一个video包是sps/pps,所以srs-librtmp的srs_h264_write_raw_frame()会忽略sps和pps之前的video包,然后返回一个错误码,用户只要忽略这个错误码即可。
  • srs_h264_is_duplicated_sps_error:这个因为IP摄像头在每个I帧前都插入sps和pps,这些重复的sps和pps会导致hls频繁的插入discontinue信息,所以srs-librtmp只有在sps和pps都变化时才发送新的sequence header包,而不是每次都发送。所以sps重复时会返回一个错误码,用户忽略这个错误即可。
  • srs_h264_is_duplicated_pps_error:这个和上面的错误一样,是pps重复,用户忽略即可。

目前的接口声明如下,最新的接口声明以代码为准:

/**
* write h.264 raw frame over RTMP to rtmp server.
* @param frames the input h264 raw data, encoded h.264 I/P/B frames data.
*       frames can be one or more than one frame,
*       each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, 
*       for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40)
*       about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
* @paam frames_size the size of h264 raw data. 
*       assert frames_size > 0, at least has 1 bytes header.
* @param dts the dts of h.264 raw data.
* @param pts the pts of h.264 raw data.
* 
* @remark, user should free the frames.
* @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms.
* @remark, cts = pts - dts
* @remark, use srs_h264_startswith_annexb to check whether frame is annexb format.
* @example /trunk/research/librtmp/srs_h264_raw_publish.c
* @see https://github.com/winlinvip/simple-rtmp-server/issues/66
* 
* @return 0, success; otherswise, failed.
*       for dvbsp error, @see srs_h264_is_dvbsp_error().
*       for duplictated sps error, @see srs_h264_is_duplicated_sps_error().
*       for duplictated pps error, @see srs_h264_is_duplicated_pps_error().
*/
/**
For the example file: 
    http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw
The data sequence is:
    // SPS
    000000016742802995A014016E40
    // PPS
    0000000168CE3880
    // IFrame
    0000000165B8041014C038008B0D0D3A071.....
    // PFrame
    0000000141E02041F8CDDC562BBDEFAD2F.....
User can send the SPS+PPS, then each frame:
    // SPS+PPS
    srs_h264_write_raw_frames('000000016742802995A014016E400000000168CE3880', size, dts, pts)
    // IFrame
    srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts)
    // PFrame
    srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts)
User also can send one by one:
    // SPS
    srs_h264_write_raw_frames('000000016742802995A014016E4', size, dts, pts)
    // PPS
    srs_h264_write_raw_frames('00000000168CE3880', size, dts, pts)
    // IFrame
    srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts)
    // PFrame
    srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) 
*/
extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, 
    char* frames, int frames_size, u_int32_t dts, u_int32_t pts
);
/**
* whether error_code is dvbsp(drop video before sps/pps/sequence-header) error.
*
* @see https://github.com/winlinvip/simple-rtmp-server/issues/203
* @example /trunk/research/librtmp/srs_h264_raw_publish.c
* @remark why drop video?
*       some encoder, for example, ipcamera, will send sps/pps before each IFrame,
*       so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header),
*       this will cause SRS server to disable HLS.
*/
extern srs_h264_bool srs_h264_is_dvbsp_error(int error_code);
/**
* whether error_code is duplicated sps error.
* 
* @see https://github.com/winlinvip/simple-rtmp-server/issues/204
* @example /trunk/research/librtmp/srs_h264_raw_publish.c
*/
extern srs_h264_bool srs_h264_is_duplicated_sps_error(int error_code);
/**
* whether error_code is duplicated pps error.
* 
* @see https://github.com/winlinvip/simple-rtmp-server/issues/204
* @example /trunk/research/librtmp/srs_h264_raw_publish.c
*/
extern srs_h264_bool srs_h264_is_duplicated_pps_error(int error_code);

srs-librtmp会将h.264包打包成RTMP包,不用用户每次都要处理。

你可能感兴趣的:(video,服务器,RTMP,h.264,摄像头)