直播流媒体协议中,HLS和RTMP协议是两大主流协议。而众所周知的原因,RTMP在许多年前就已经停止拓展和更新,因此标准一直无法支持HEVC的编码格式。目前国内的CDN还有金山云等已经对RTMP进行了标准扩展,播放器上ijkplayer也扩展了该修改。
具体FFmpeg的修改代码如下:
-
From e40fcb1113cb1c93c48b8ef74b8aec6437f23d84 Mon Sep
17
00:
00:
00
2001
-
From: Franken Zeng <zengfanping@kingsoft.com>
-
Date: Wed,
14 Jun
2017
21:
33:
54 +
0800
-
Subject: [PATCH] The RTMP protocol extensions
for H
.265/HEVC
-
-
---
-
libavformat/flv.h |
1 +
-
libavformat/flvdec.c |
16 +++++++++++++---
-
libavformat/flvenc.c |
29 ++++++++++++++++++++---------
-
3 files changed,
34 insertions(+),
12 deletions(-)
-
-
diff --git a/libavformat/flv.h b/libavformat/flv.h
-
index df5ce3d17f8.
.089bc76972d
100644
-
--- a/libavformat/flv.h
-
+++ b/libavformat/flv.h
-
@@
-109,
6 +
109,
7 @@
enum {
-
FLV_CODECID_H264 =
7,
-
FLV_CODECID_REALH263=
8,
-
FLV_CODECID_MPEG4 =
9,
-
+ FLV_CODECID_HEVC =
12,
-
};
-
-
enum {
-
diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
-
index
2d89bef15f2..c8073b88a92
100644
-
--- a/libavformat/flvdec.c
-
+++ b/libavformat/flvdec.c
-
@@
-36,
6 +
36,
7 @@
-
#include "internal.h"
-
#include "avio_internal.h"
-
#include "flv.h"
-
+
#include "hevc.h"
-
-
#define VALIDATE_INDEX_TS_THRESH 2500
-
-
@@
-291,
6 +
292,
8 @@
static int flv_same_video_codec(AVCodecParameters *vpar, int flags)
-
return vpar->codec_id == AV_CODEC_ID_VP6A;
-
case FLV_CODECID_H264:
-
return vpar->codec_id == AV_CODEC_ID_H264;
-
+
case FLV_CODECID_HEVC:
-
+
return vpar->codec_id == AV_CODEC_ID_HEVC;
-
default:
-
return vpar->codec_tag == flv_codecid;
-
}
-
@@
-340,
6 +
343,
11 @@
static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
-
par->codec_id = AV_CODEC_ID_MPEG4;
-
ret = 3;
-
break;
-
+ case FLV_CODECID_HEVC:
-
+ par->codec_id = AV_CODEC_ID_HEVC;
-
+ vstream->need_parsing = AVSTREAM_PARSE_NONE;
-
+ ret = 3; // not 4, reading packet type will consume one byte
-
+ break;
-
default:
-
avpriv_request_sample(s, "Video codec (%x)", flv_codecid);
-
par->codec_tag = flv_codecid;
-
@@
-1149,
10 +
1157,
12 @@
static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
-
-
if
(st->codecpar->codec_id == AV_CODEC_ID_AAC ||
-
st->codecpar->codec_id == AV_CODEC_ID_H264 ||
-
- st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
-
+ st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
-
+ st->codecpar->codec_id == AV_CODEC_ID_HEVC) {
-
int type = avio_r8(s->pb);
-
size--;
-
-
if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
-
+
if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4
-
+ || st->codecpar->codec_id == AV_CODEC_ID_HEVC) {
-
// sign extension
-
int32_t cts = (avio_rb24(s->pb) +
0xff800000) ^
0xff800000;
-
pts = dts + cts;
-
@@
-1168,
7 +
1178,
7 @@
static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
-
}
-
}
-
if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||
-
- st->codecpar->codec_id == AV_CODEC_ID_H264)) {
-
+ st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC)) {
-
AVDictionaryEntry *t;
-
-
if (st->codecpar->extradata) {
-
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
-
index
899b07ea7be..eb955e5b451
100644
-
--- a/libavformat/flvenc.c
-
+++ b/libavformat/flvenc.c
-
@@
-34,
6 +
34,
7 @@
-
#include "libavutil/opt.h"
-
#include "libavcodec/put_bits.h"
-
#include "libavcodec/aacenctab.h"
-
+
#include "hevc.h"
-
-
-
static
const AVCodecTag flv_video_codec_ids[] = {
-
@@
-46,
6 +
47,
7 @@
static
const AVCodecTag flv_video_codec_ids[] = {
-
{ AV_CODEC_ID_VP6, FLV_CODECID_VP6 },
-
{ AV_CODEC_ID_VP6A, FLV_CODECID_VP6A },
-
{ AV_CODEC_ID_H264, FLV_CODECID_H264 },
-
+ { AV_CODEC_ID_HEVC, FLV_CODECID_HEVC },
-
{ AV_CODEC_ID_NONE,
0 }
-
};
-
-
@@
-486,
7 +
488,
7 @@
static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par) {
-
FLVContext *flv = s->priv_data;
-
-
if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
-
- || par->codec_id == AV_CODEC_ID_MPEG4) {
-
+ || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {
-
int64_t pos;
-
avio_w8(pb,
-
par->codec_type == AVMEDIA_TYPE_VIDEO ?
-
@@
-533,
7 +
535,
11 @@
static
void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par) {
-
avio_w8(pb, par->codec_tag | FLV_FRAME_KEY);
// flags
-
avio_w8(pb,
0);
// AVC sequence header
-
avio_wb24(pb,
0);
// composition time
-
- ff_isom_write_avcc(pb, par->extradata, par->extradata_size);
-
+
if (par->codec_id == AV_CODEC_ID_HEVC) {
-
+ ff_isom_write_hvcc(pb, par->extradata, par->extradata_size,
0);
-
+ }
else {
-
+ ff_isom_write_avcc(pb, par->extradata, par->extradata_size);
-
+ }
-
}
-
data_size = avio_tell(pb) - pos;
-
avio_seek(pb, -data_size -
10, SEEK_CUR);
-
@@
-836,
7 +
842,
7 @@
static
int flv_write_trailer(AVFormatContext *s)
-
AVCodecParameters *par = s->streams[i]->codecpar;
-
FLVStreamContext *sc = s->streams[i]->priv_data;
-
if (par->codec_type == AVMEDIA_TYPE_VIDEO &&
-
- (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4))
-
+ (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC))
-
put_avc_eos_tag(pb, sc->last_ts);
-
}
-
}
-
@@
-880,
15 +
886,
16 @@
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
-
int64_t cur_offset = avio_tell(pb);
-
-
if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A ||
-
- par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC)
-
+ par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC) {
-
flags_size =
2;
-
-
else
if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4)
-
+ }
else
if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {
-
flags_size =
5;
-
-
else
-
+ }
else {
-
flags_size =
1;
-
+ }
-
-
if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
-
- || par->codec_id == AV_CODEC_ID_MPEG4) {
-
+ || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {
-
int side_size =
0;
-
uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
-
if (side && side_size >
0 && (side_size != par->extradata_size ||
memcmp(side, par->extradata, side_size))) {
-
@@
-951,
6 +
958,
10 @@
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
-
if
(par->extradata_size > 0 && *(uint8_t*)par->extradata !=
1)
-
if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) <
0)
-
return ret;
-
+ }
else
if (par->codec_id == AV_CODEC_ID_HEVC) {
-
+
if (par->extradata_size >
0 && *(
uint8_t*)par->extradata !=
1)
-
+
if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size,
0,
NULL)) <
0)
-
+
return ret;
-
}
else
if (par->codec_id == AV_CODEC_ID_AAC && pkt->size >
2 &&
-
(AV_RB16(pkt->data) &
0xfff0) ==
0xfff0) {
-
if (!s->streams[pkt->stream_index]->nb_frames) {
-
@@
-1021,
9 +
1032,
9 @@
static
int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
-
else
-
avio_w8(pb, ((FFALIGN(par->width,
16) - par->width) <<
4) |
-
(FFALIGN(par->height,
16) - par->height));
-
- }
else
if (par->codec_id == AV_CODEC_ID_AAC)
-
+ }
else
if (par->codec_id == AV_CODEC_ID_AAC) {
-
avio_w8(pb,
1);
// AAC raw
-
-
else
if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) {
-
+ }
else
if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {
-
avio_w8(pb,
1);
// AVC NALU
-
avio_wb24(pb, pkt->pts - pkt->dts);
-
}
通过该项修改即可拓展FFmpeg中RTMP对于HEVC的支持。
我的上一篇文章,《FFmpeg+Nginx搭建RTMP直播推流服务》记录了FFmpeg+Nginx的RTMP的搭建方式。
但是由于服务端Nginx并没有添加HEVC下重发flv的header头部,因此播放器端只有在从流的最开始才能够成功的解码,否则会出现缺少解码参数集导致无法成功解码。解决办法有两个:
1. 在FFmpeg代码中每一包都手动添加解码参数集于flv的header中,修改如下:
ff_write_packet中
-
if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264
-
|| par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC) {
-
int side_size =
0;
-
uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
-
if (side && side_size >
0 && (side_size != par->extradata_size ||
memcmp(side, par->extradata, side_size))) {
-
av_free(par->extradata);
-
par->extradata = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE);
-
if (!par->extradata) {
-
par->extradata_size =
0;
-
return AVERROR(ENOMEM);
-
}
-
memcpy(par->extradata, side, side_size);
-
par->extradata_size = side_size;
-
flv_write_codec_header(s, par, pkt->dts);
-
}
else {
-
flv_write_codec_header(s, par, pkt->dts);
-
}
-
}
2. 修改Nginx,添加HEVC的支持。这部分暂无时间修改。
转载自:https://blog.csdn.net/smallhujiu/article/details/81703434