分析过程
消息流程:
epoll_read callback()
ngx_rtmp_recv (解析完rtmp chunkheader,得到ngx_rtmp_header_t (mlen,type,timestamp))
ngx_rtmp_receive_message (消息分发: cmcf->events[h->type]存着各模块的handler函数)
——ngx_rtmp_hls_video(hls模块的处理函数)
——ngx_rtmp_record_av(record模块的处理函数,流可以用record来做对比)
关于rtmp chunk的格式canz\\:http://www.cnblogs.com/android-blogs/p/5650771.html
关于flv格式可以参照: http://blog.csdn.net/bsplover/article/details/7426511 里面有详细说明各个tag
下面是代码:
static ngx_int_t
ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_hls_app_conf_t *hacf;
ngx_rtmp_hls_ctx_t *ctx;
ngx_rtmp_codec_ctx_t *codec_ctx;
u_char *p;
uint8_t fmt, ftype, htype, nal_type, src_nal_type;
uint32_t len, rlen;
ngx_buf_t out, *b;
uint32_t cts;
ngx_rtmp_mpegts_frame_t frame;
ngx_uint_t nal_bytes;
ngx_int_t aud_sent, sps_pps_sent, boundary;
static u_char buffer[NGX_RTMP_HLS_BUFSIZE];
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (hacf == NULL || !hacf->hls || ctx == NULL || codec_ctx == NULL ||
codec_ctx->avc_header == NULL || h->mlen < 1)
{
return NGX_OK;
}
/* Only H264 is supported */
if (codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) {
return NGX_OK;
}
p = in->buf->pos;
if (ngx_rtmp_hls_copy(s, &fmt, &p, 1, &in) != NGX_OK) {
return NGX_ERROR;
}
/* 1: keyframe (IDR)
* 2: inter frame
* 3: disposable inter frame */
//第一字节 4bit:帧类型 4bit:编码器ID
ftype = (fmt & 0xf0) >> 4;
//这个博客中有讲到flv的videotag中的下面四个字节AVCPacketType1 和 CompositionTime3
// http://blog.csdn.net/bsplover/article/details/7426511
//IF AVCPacketType == 0 AVCDecoderConfigurationRecord(AVC sequence header)
//IF AVCPacketType == 1 One or more NALUs (Full frames are required)
//AVCDecoderConfigurationRecord.包含着是H.264解码相关比较重要的sps和pps信息
//Composition time offset
/* H264 HDR/PICT */
if (ngx_rtmp_hls_copy(s, &htype, &p, 1, &in) != NGX_OK) {
return NGX_ERROR;
}
/* proceed only with PICT */
if (htype != 1) {
return NGX_OK;
}
/* 3 bytes: decoder delay */
if (ngx_rtmp_hls_copy(s, &cts, &p, 3, &in) != NGX_OK) {
return NGX_ERROR;
}
cts = ((cts & 0x00FF0000) >> 16) | ((cts & 0x000000FF) << 16) |
(cts & 0x0000FF00);
ngx_memzero(&out, sizeof(out));
out.start = buffer;
out.end = buffer + sizeof(buffer);
out.pos = out.start;
out.last = out.pos;
nal_bytes = codec_ctx->avc_nal_bytes;
aud_sent = 0;
sps_pps_sent = 0;
while (in) {
if (ngx_rtmp_hls_copy(s, &rlen, &p, nal_bytes, &in) != NGX_OK) {
return NGX_OK;
}
len = 0;
ngx_rtmp_rmemcpy(&len, &rlen, nal_bytes);
if (len == 0) {
continue;
}
if (ngx_rtmp_hls_copy(s, &src_nal_type, &p, 1, &in) != NGX_OK) {
return NGX_OK;
}
nal_type = src_nal_type & 0x1f;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: h264 NAL type=%ui, len=%uD",
(ngx_uint_t) nal_type, len);
if (nal_type >= 7 && nal_type <= 9) {
if (ngx_rtmp_hls_copy(s, NULL, &p, len - 1, &in) != NGX_OK) {
return NGX_ERROR;
}
continue;
}
if (!aud_sent) {
switch (nal_type) {
case 1:
case 5:
case 6:
if (ngx_rtmp_hls_append_aud(s, &out) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"hls: error appending AUD NAL");
}
case 9:
aud_sent = 1;
break;
}
}
switch (nal_type) {
case 1:
sps_pps_sent = 0;
break;
case 5:
if (sps_pps_sent) {
break;
}
if (ngx_rtmp_hls_append_sps_pps(s, &out) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"hls: error appenging SPS/PPS NALs");
}
sps_pps_sent = 1;
break;
}
/* AnnexB prefix */
if (out.end - out.last < 5) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"hls: not enough buffer for AnnexB prefix");
return NGX_OK;
}
/* first AnnexB prefix is long (4 bytes) */
if (out.last == out.pos) {
*out.last++ = 0;
}
*out.last++ = 0;
*out.last++ = 0;
*out.last++ = 1;
*out.last++ = src_nal_type;
/* NAL body */
if (out.end - out.last < (ngx_int_t) len) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"hls: not enough buffer for NAL");
return NGX_OK;
}
if (ngx_rtmp_hls_copy(s, out.last, &p, len - 1, &in) != NGX_OK) {
return NGX_ERROR;
}
out.last += (len - 1);
}
ngx_memzero(&frame, sizeof(frame));
frame.cc = ctx->video_cc;
frame.dts = (uint64_t) h->timestamp * 90;
frame.pts = frame.dts + cts * 90;
frame.pid = 0x100;
frame.sid = 0xe0;
frame.key = (ftype == 1);
/*
* start new fragment if
* - we have video key frame AND
* - we have audio buffered or have no audio at all or stream is closed
*/
b = ctx->aframe;
boundary = frame.key && (codec_ctx->aac_header == NULL || !ctx->opened ||
(b && b->last > b->pos));
ngx_rtmp_hls_update_fragment(s, frame.dts, boundary, 1);
if (!ctx->opened) {
return NGX_OK;
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: video pts=%uL, dts=%uL", frame.pts, frame.dts);
if (ngx_rtmp_mpegts_write_frame(&ctx->file, &frame, &out) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"hls: video frame failed");
}
ctx->video_cc = frame.cc;
return NGX_OK;
}