H265Rtp.cpp 12.5 KB
Newer Older
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
3
 *
4
 * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
5
 *
xiongziliang committed
6 7 8
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
9 10
 */

xiongziliang committed
11
#include "H265Rtp.h"
12
#include "Common/config.h"
13 14
namespace mediakit{

15
//https://datatracker.ietf.org/doc/rfc7798/
xiongziliang committed
16 17 18 19 20 21 22 23 24 25 26 27
//H265 nalu 头两个字节的定义
/*
 0               1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |F|    Type   |  LayerId  | TID |
 +-------------+-----------------+
 Forbidden zero(F) : 1 bit
 NAL unit type(Type) : 6 bits
 NUH layer ID(LayerId) : 6 bits
 NUH temporal ID plus 1 (TID) : 3 bits
*/
28 29

H265RtpDecoder::H265RtpDecoder() {
xiongziliang committed
30
    _frame = obtainFrame();
31 32
}

33 34
H265Frame::Ptr H265RtpDecoder::obtainFrame() {
    auto frame = FrameImp::create<H265Frame>();
35
    frame->_prefix_size = 4;
36 37 38
    return frame;
}

xiongziliang committed
39 40 41 42 43 44
#define AV_RB16(x)                           \
    ((((const uint8_t*)(x))[0] << 8) |          \
      ((const uint8_t*)(x))[1])

#define CHECK_SIZE(total, size, ret) \
        if (total < size) {     \
45
            WarnL << "invalid rtp data size:" << total << " < " << size << ",rtp:\r\n" << rtp->dumpString(); _gop_dropped = true;  return ret; \
xiongziliang committed
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
        }

// 4.4.2. Aggregation Packets (APs) (p25)
/*
 0               1               2               3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          RTP Header                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      PayloadHdr (Type=48)     |           NALU 1 DONL         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           NALU 1 Size         |            NALU 1 HDR         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                         NALU 1 Data . . .                     |
|                                                               |
+     . . .     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |  NALU 2 DOND  |            NALU 2 Size        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          NALU 2 HDR           |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            NALU 2 Data        |
|                                                               |
|         . . .                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :    ...OPTIONAL RTP padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ziyue committed
72
bool H265RtpDecoder::unpackAp(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp){
xiongziliang committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    bool have_key_frame = false;
    //忽略PayloadHdr
    CHECK_SIZE(size, 2, have_key_frame);
    ptr += 2;
    size -= 2;

    while (size) {
        if (_using_donl_field) {
            CHECK_SIZE(size, 2, have_key_frame);
            uint16_t donl = AV_RB16(ptr);
            size -= 2;
            ptr += 2;
        }
        CHECK_SIZE(size, 2, have_key_frame);
        uint16_t nalu_size = AV_RB16(ptr);
        size -= 2;
        ptr += 2;
        CHECK_SIZE(size, nalu_size, have_key_frame)
91
        if (singleFrame(rtp, ptr, nalu_size, stamp)) {
xiongziliang committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
            have_key_frame = true;
        }
        size -= nalu_size;
        ptr += nalu_size;
    }
    return have_key_frame;
}

// 4.4.3. Fragmentation Units (p29)
/*
 0               1               2               3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     PayloadHdr (Type=49)      |    FU header  |  DONL (cond)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|  DONL (cond)  |                                               |
|-+-+-+-+-+-+-+-+                                               |
|                           FU payload                          |
|                                                               |
|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :    ...OPTIONAL RTP padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|   FuType  |
+---------------+
*/

ziyue committed
122
bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp, uint16_t seq){
xiongziliang committed
123 124 125 126 127 128 129 130 131
    CHECK_SIZE(size, 4, false);
    auto s_bit = ptr[2] >> 7;
    auto e_bit = (ptr[2] >> 6) & 0x01;
    auto type = ptr[2] & 0x3f;
    if (s_bit) {
        //该帧的第一个rtp包
        _frame->_buffer.assign("\x00\x00\x00\x01", 4);
        _frame->_buffer.push_back((type << 1) | (ptr[0] & 0x81));
        _frame->_buffer.push_back(ptr[1]);
132 133 134 135 136 137 138
        _frame->_pts = stamp;
        _fu_dropped = false;
    }

    if (_fu_dropped) {
        //该帧不完整
        return false;
xiongziliang committed
139 140
    }

141
    if (!s_bit && seq != (uint16_t) (_last_seq + 1)) {
xiongziliang committed
142
        //中间的或末尾的rtp包,其seq必须连续,否则说明rtp丢包,那么该帧不完整,必须得丢弃
143
        _fu_dropped = true;
xiongziliang committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        _frame->_buffer.clear();
        return false;
    }

    //跳过PayloadHdr +  FU header
    ptr += 3;
    size -= 3;
    if (_using_donl_field) {
        //DONL确保不少于2个字节
        CHECK_SIZE(size, 2, false);
        uint16_t donl = AV_RB16(ptr);
        size -= 2;
        ptr += 2;
    }

    CHECK_SIZE(size, 1, false);

161 162 163
    //后面追加数据
    _frame->_buffer.append((char *) ptr, size);

xiongziliang committed
164 165 166 167 168
    if (!e_bit) {
        //非末尾包
        return s_bit ? _frame->keyFrame() : false;
    }

169 170
    //确保下一次fu必须收到第一个包
    _fu_dropped = true;
xiongziliang committed
171
    //该帧最后一个rtp包
172
    outputFrame(rtp, _frame);
xiongziliang committed
173
    return false;
174 175
}

176 177 178 179 180 181 182 183 184 185 186 187
bool H265RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) {
    auto seq = rtp->getSeq();
    auto ret = decodeRtp(rtp);
    if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) {
        _gop_dropped = true;
        WarnL << "start drop h265 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
    }
    _last_seq = seq;
    return ret;
}

bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
188 189 190 191 192
    auto payload_size = rtp->getPayloadSize();
    if (payload_size <= 0) {
        //无实际负载
        return false;
    }
xiongziliang committed
193 194 195
    auto frame = rtp->getPayload();
    auto stamp = rtp->getStampMS();
    auto seq = rtp->getSeq();
196
    int nal = H265_TYPE(frame[0]);
197

198
    switch (nal) {
xiongziliang committed
199 200
        case 48:
            // aggregated packet (AP) - with two or more NAL units
201
            return unpackAp(rtp, frame, payload_size, stamp);
202 203

        case 49:
204
            // fragmentation unit (FU)
205
            return mergeFu(rtp, frame, payload_size, stamp, seq);
206 207 208 209

        default: {
            if (nal < 48) {
                // Single NAL Unit Packets (p24)
210
                return singleFrame(rtp, frame, payload_size, stamp);
211 212 213 214 215
            }
            _gop_dropped = true;
            WarnL << "不支持该类型的265 RTP包, nal type" << nal << ", rtp:\r\n" << rtp->dumpString();
            return false;
        }
216 217 218
    }
}

ziyue committed
219
bool H265RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssize_t size, uint64_t stamp){
xiongziliang committed
220 221 222 223
    _frame->_buffer.assign("\x00\x00\x00\x01", 4);
    _frame->_buffer.append((char *) ptr, size);
    _frame->_pts = stamp;
    auto key = _frame->keyFrame();
224
    outputFrame(rtp, _frame);
xiongziliang committed
225 226 227
    return key;
}

228
void H265RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr &frame) {
229 230 231 232 233 234 235
    if (frame->dropAble()) {
        //不参与dts生成
        frame->_dts = frame->_pts;
    } else {
        //rtsp没有dts,那么根据pts排序算法生成dts
        _dts_generator.getDts(frame->_pts, frame->_dts);
    }
236 237 238 239 240 241 242 243

    if (frame->keyFrame() && _gop_dropped) {
        _gop_dropped = false;
        InfoL << "new gop received, rtp:\r\n" << rtp->dumpString();
    }
    if (!_gop_dropped) {
        RtpCodec::inputFrame(frame);
    }
xiongziliang committed
244
    _frame = obtainFrame();
245 246 247 248 249 250 251
}

////////////////////////////////////////////////////////////////////////

H265RtpEncoder::H265RtpEncoder(uint32_t ui32Ssrc,
                               uint32_t ui32MtuSize,
                               uint32_t ui32SampleRate,
xiongziliang committed
252
                               uint8_t ui8PayloadType,
253 254 255 256
                               uint8_t ui8Interleaved) :
        RtpInfo(ui32Ssrc,
                ui32MtuSize,
                ui32SampleRate,
xiongziliang committed
257
                ui8PayloadType,
258 259 260
                ui8Interleaved) {
}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
void H265RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
    auto max_size = getMaxSize() - 3;
    auto nal_type = H265_TYPE(ptr[0]); //获取NALU的5bit 帧类型
    unsigned char s_e_flags;
    bool fu_start = true;
    bool mark_bit = false;
    size_t offset = 2;
    while (!mark_bit) {
        if (len <= offset + max_size) {
            // FU end
            mark_bit = true;
            max_size = len - offset;
            s_e_flags = (1 << 6) | nal_type;
        } else if (fu_start) {
            // FU start
            s_e_flags = (1 << 7) | nal_type;
        } else {
            // FU mid
            s_e_flags = nal_type;
        }

        {
            // 传入nullptr先不做payload的内存拷贝
            auto rtp = makeRtp(getTrackType(), nullptr, max_size + 3, mark_bit, pts);
            // rtp payload 负载部分
            uint8_t *payload = rtp->getPayload();
            // FU 第1个字节,表明为FU
            payload[0] = 49 << 1;
            // FU 第2个字节貌似固定为1
            payload[1] = ptr[1]; // 1;
            // FU 第3个字节
            payload[2] = s_e_flags;
            // H265 数据
            memcpy(payload + 3, ptr + offset, max_size);
            // 输入到rtp环形缓存
            RtpCodec::inputRtp(rtp, fu_start && gop_pos);
        }

        offset += max_size;
        fu_start = false;
    }
}

void H265RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
    if (len + 3 <= getMaxSize()) {
        //signal-nalu 
        RtpCodec::inputRtp(makeRtp(getTrackType(), ptr, len, is_mark, pts), gop_pos);
    } else {
        //FU-A模式
        packRtpFu(ptr, len, pts, is_mark, gop_pos);
    }
}
void H265RtpEncoder::insertConfigFrame(uint64_t pts){
     if (!_sps || !_pps || !_vps) {
        WarnL<<" not ok";
        return;
    }
    //gop缓存从vps 开始,vps ,sps、pps后面还有时间戳相同的关键帧,所以mark bit为false
    packRtp(_vps->data() + _vps->prefixSize(), _vps->size() - _vps->prefixSize(), pts, false, true);
    packRtp(_sps->data() + _sps->prefixSize(), _sps->size() - _sps->prefixSize(), pts, false, false);
    packRtp(_pps->data() + _pps->prefixSize(), _pps->size() - _pps->prefixSize(), pts, false, false);
    
}
bool H265RtpEncoder::inputFrame_l(const Frame::Ptr &frame, bool is_mark){
     if (frame->keyFrame()) {
        //保证每一个关键帧前都有SPS PPS VPS
        insertConfigFrame(frame->pts());
    }
    packRtp(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(), frame->pts(), is_mark, false);
    return true;
}
332
bool H265RtpEncoder::inputFrame(const Frame::Ptr &frame) {
333 334 335
    auto ptr = (uint8_t *) frame->data() + frame->prefixSize();
    auto nal_type = H265_TYPE(ptr[0]); //获取NALU的5bit 帧类型

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
    switch (nal_type) {
        case H265Frame::NAL_SPS: {
            _sps = Frame::getCacheAbleFrame(frame);
            return true;
        }
        case H265Frame::NAL_PPS: {
            _pps = Frame::getCacheAbleFrame(frame);
            return true;
        }
        case H265Frame::NAL_VPS:{
            _vps = Frame::getCacheAbleFrame(frame);
            return true;
        }
        default: break;
    }
351

352 353 354 355
    GET_CONFIG(int,lowLatency,Rtp::kLowLatency);
    if (lowLatency) { // 低延迟模式
        if (_last_frame) {
            flush();
356
        }
357
        inputFrame_l(frame, true);
358
    } else {
359 360 361 362 363 364 365 366 367 368 369 370 371 372
        if (_last_frame) {
            //如果时间戳发生了变化,那么markbit才置true
            inputFrame_l(_last_frame, _last_frame->pts() != frame->pts());
        }
        _last_frame = Frame::getCacheAbleFrame(frame);
    }
    return true;
}

void H265RtpEncoder::flush() {
    if (_last_frame) {
        // 如果时间戳发生了变化,那么markbit才置true
        inputFrame_l(_last_frame, true);
        _last_frame = nullptr;
373 374 375
    }
}

376
}//namespace mediakit