H264Rtp.cpp 8.96 KB
Newer Older
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
3 4 5
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
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
 */
xiongziliang committed
10

xiongziliang committed
11
#include "H264Rtp.h"
xiongziliang committed
12

xiongziliang committed
13
namespace mediakit{
14

xiongziliang committed
15 16 17 18 19 20 21
typedef struct {
    unsigned S :1;
    unsigned E :1;
    unsigned R :1;
    unsigned type :5;
} FU;

22
static bool MakeFU(uint8_t in, FU &fu) {
xiongziliang committed
23 24 25 26 27 28 29 30 31 32
    fu.S = in >> 7;
    fu.E = (in >> 6) & 0x01;
    fu.R = (in >> 5) & 0x01;
    fu.type = in & 0x1f;
    if (fu.R != 0) {
        return false;
    }
    return true;
}

33
H264RtpDecoder::H264RtpDecoder() {
34
    _h264frame = obtainFrame();
35 36 37 38
}

H264Frame::Ptr  H264RtpDecoder::obtainFrame() {
    //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
39
    auto frame = ResourcePoolHelper<H264Frame>::obtainObj();
40 41
    frame->_buffer.clear();
    frame->_prefix_size = 4;
42 43 44
    return frame;
}

xiongziliang committed
45
bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
xiongziliang committed
46
    return decodeRtp(rtp);
xiongziliang committed
47 48
}

49
bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
xiongziliang committed
50 51 52 53 54 55 56 57
    /**
     * h264帧类型
     * Type==1:P/B frame
     * Type==5:IDR frame
     * Type==6:SEI frame
     * Type==7:SPS frame
     * Type==8:PPS frame
     */
58 59
    /*
    RTF3984 5.2节  Common Structure of the RTP Payload Format
3503207480@qq.com committed
60 61
    Table 1.  Summary of NAL unit types and their payload structures

62 63 64 65 66 67 68 69 70 71 72 73
       Type   Packet    Type name                        Section
       ---------------------------------------------------------
       0      undefined                                    -
       1-23   NAL unit  Single NAL unit packet per H.264   5.6
       24     STAP-A    Single-time aggregation packet     5.7.1
       25     STAP-B    Single-time aggregation packet     5.7.1
       26     MTAP16    Multi-time aggregation packet      5.7.2
       27     MTAP24    Multi-time aggregation packet      5.7.2
       28     FU-A      Fragmentation unit                 5.8
       29     FU-B      Fragmentation unit                 5.8
       30-31  undefined                                    -
    */
xiongziliang committed
74 75
    const uint8_t *frame = (uint8_t *) rtppack->data() + rtppack->offset;
    int length = rtppack->size() - rtppack->offset;
76 77
    int nal_type = *frame & 0x1F;
    int nal_suffix = *frame & (~0x1F);
xiongziliang committed
78

79
    if (nal_type >= 0 && nal_type < 24) {
xiongziliang committed
80
        //a full frame
81
        _h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
82
        _h264frame->_buffer.append((char *) frame, length);
83
        _h264frame->_pts = rtppack->timeStamp;
84
        auto key = _h264frame->keyFrame();
85
        onGetH264(_h264frame);
86
        return (key); //i frame
xiongziliang committed
87 88
    }

89
    switch (nal_type){
90 91 92 93
        case 24:{
            // 24 STAP-A   单一时间的组合包
            bool haveIDR = false;
            auto ptr = frame + 1;
94
            while (true) {
95 96 97 98 99 100 101 102 103 104 105
                int off = ptr - frame;
                if (off >= length) {
                    break;
                }
                //获取当前nalu的大小
                uint16_t len = *ptr++;
                len <<= 8;
                len |= *ptr++;
                if (off + len > length) {
                    break;
                }
106 107
                if (len > 0) {
                    //有有效数据
108
                    _h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
109
                    _h264frame->_buffer.append((char *) ptr, len);
110
                    _h264frame->_pts = rtppack->timeStamp;
111
                    if ((ptr[0] & 0x1F) == H264Frame::NAL_IDR) {
112 113 114 115 116 117 118
                        haveIDR = true;
                    }
                    onGetH264(_h264frame);
                }
                ptr += len;
            }
            return haveIDR;
xiongziliang committed
119 120
        }

121 122 123 124
        case 28:{
            //FU-A
            FU fu;
            MakeFU(frame[1], fu);
125
            if (fu.S) {
3503207480@qq.com committed
126
                //该帧的第一个rtp包  FU-A start
127
                _h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
128 129
                _h264frame->_buffer.push_back(nal_suffix | fu.type);
                _h264frame->_buffer.append((char *) frame + 2, length - 2);
130
                _h264frame->_pts = rtppack->timeStamp;
131 132 133
                //该函数return时,保存下当前sequence,以便下次对比seq是否连续
                _lastSeq = rtppack->sequence;
                return _h264frame->keyFrame();
134 135
            }

136 137
            if (rtppack->sequence != _lastSeq + 1 && rtppack->sequence != 0) {
                //中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃
138
                _h264frame->_buffer.clear();
xiongziliang committed
139
                WarnL << "rtp丢包: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃";
140 141
                return false;
            }
142 143

            if (!fu.E) {
3503207480@qq.com committed
144
                //该帧的中间rtp包  FU-A mid
145
                _h264frame->_buffer.append((char *) frame + 2, length - 2);
146 147 148
                //该函数return时,保存下当前sequence,以便下次对比seq是否连续
                _lastSeq = rtppack->sequence;
                return false;
149
            }
150

3503207480@qq.com committed
151
            //该帧最后一个rtp包  FU-A end
152
            _h264frame->_buffer.append((char *) frame + 2, length - 2);
153
            _h264frame->_pts = rtppack->timeStamp;
154
            onGetH264(_h264frame);
155
            return false;
xiongziliang committed
156
        }
157

158
        default: {
159 160 161 162 163 164 165
            // 29 FU-B     单NAL单元B模式
            // 25 STAP-B   单一时间的组合包
            // 26 MTAP16   多个时间的组合包
            // 27 MTAP24   多个时间的组合包
            // 0 udef
            // 30 udef
            // 31 udef
166
            WarnL << "不支持的rtp类型:" << (int) nal_type << " " << rtppack->sequence;
167
            return false;
xiongziliang committed
168 169 170 171
        }
    }
}

xiongziliang committed
172
void H264RtpDecoder::onGetH264(const H264Frame::Ptr &frame) {
xiongziliang committed
173 174 175 176
    //rtsp没有dts,那么根据pts排序算法生成dts
    _dts_generator.getDts(frame->_pts,frame->_dts);
    //写入环形缓存
    RtpCodec::inputFrame(frame);
177
    _h264frame = obtainFrame();
xiongziliang committed
178
}
xiongziliang committed
179

180

xiongziliang committed
181 182 183 184 185
////////////////////////////////////////////////////////////////////////

H264RtpEncoder::H264RtpEncoder(uint32_t ui32Ssrc,
                               uint32_t ui32MtuSize,
                               uint32_t ui32SampleRate,
xiongziliang committed
186
                               uint8_t ui8PayloadType,
xiongziliang committed
187
                               uint8_t ui8Interleaved) :
188 189 190
        RtpInfo(ui32Ssrc,
                ui32MtuSize,
                ui32SampleRate,
xiongziliang committed
191
                ui8PayloadType,
192
                ui8Interleaved) {
xiongziliang committed
193 194
}

195
void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
196
    GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS);
197 198 199 200 201
    auto ptr = frame->data() + frame->prefixSize();
    auto pts = frame->pts() % cycleMS;
    auto len = frame->size() - frame->prefixSize();
    auto nal_type = H264_TYPE(ptr[0]);
    auto max_rtp_size = _ui32MtuSize - 2;
xiongziliang committed
202

xiongziliang committed
203
    //超过MTU则按照FU-A模式打包
204
    if (len > max_rtp_size) {
xiongziliang committed
205 206 207
        //最高位bit为forbidden_zero_bit,
        //后面2bit为nal_ref_idc(帧重要程度),00:可以丢,11:不能丢
        //末尾5bit为nalu type,固定为28(FU-A)
208
        unsigned char nal_fu_a = (*((unsigned char *) ptr) & (~0x1F)) | 28;
xiongziliang committed
209
        unsigned char s_e_r_flags;
210 211 212 213 214
        bool fu_a_start = true;
        bool mark_bit = false;
        int offset = 1;
        while (!mark_bit) {
            if (len <= offset + max_rtp_size) {
xiongziliang committed
215
                //已经拆分结束
216 217
                max_rtp_size = len - offset;
                mark_bit = true;
xiongziliang committed
218
                //FU-A end
219 220
                s_e_r_flags = (1 << 6) | nal_type;
            } else if (fu_a_start) {
xiongziliang committed
221
                //FU-A start
222
                s_e_r_flags = (1 << 7) | nal_type;
xiongziliang committed
223
            } else {
xiongziliang committed
224
                //FU-A mid
225
                s_e_r_flags = nal_type;
xiongziliang committed
226
            }
227 228 229

            {
                //传入nullptr先不做payload的内存拷贝
230
                auto rtp = makeRtp(getTrackType(), nullptr, max_rtp_size + 2, mark_bit, pts);
231 232 233
                //rtp payload 负载部分
                uint8_t *payload = (uint8_t*)rtp->data() + rtp->offset;
                //FU-A 第1个字节
234
                payload[0] = nal_fu_a;
235
                //FU-A 第2个字节
xiongziliang committed
236
                payload[1] = s_e_r_flags;
237
                //H264 数据
238
                memcpy(payload + 2, (unsigned char *) ptr + offset, max_rtp_size);
239
                //输入到rtp环形缓存
240
                RtpCodec::inputRtp(rtp, fu_a_start && nal_type == H264Frame::NAL_IDR);
241
            }
242 243
            offset += max_rtp_size;
            fu_a_start = false;
xiongziliang committed
244 245
        }
    } else {
246 247
        //如果帧长度不超过mtu, 则按照Single NAL unit packet per H.264 方式打包
        makeH264Rtp(ptr, len, false, false, pts);
xiongziliang committed
248 249 250
    }
}

251 252
void H264RtpEncoder::makeH264Rtp(const void* data, unsigned int len, bool mark, bool gop_pos, uint32_t uiStamp) {
    RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), gop_pos);
xiongziliang committed
253 254
}

255
}//namespace mediakit