H264Rtp.cpp 8.94 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
 */
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
    const uint8_t *frame = (uint8_t *) rtppack->data() + rtppack->offset;
75
    auto 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
                size_t off = ptr - frame;
96 97 98 99 100 101 102 103 104 105
                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
            if (rtppack->sequence != (uint16_t)(_lastSeq + 1) && rtppack->sequence != 0) {
137
                //中间的或末尾的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
    auto ptr = frame->data() + frame->prefixSize();
    auto len = frame->size() - frame->prefixSize();
199
    auto pts = frame->pts() % cycleMS;
200
    auto nal_type = H264_TYPE(ptr[0]);
201
    size_t payload_size = _ui32MtuSize - 2;
xiongziliang committed
202

xiongziliang committed
203
    //超过MTU则按照FU-A模式打包
204
    if (len > payload_size + 1) {
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
        bool fu_a_start = true;
        bool mark_bit = false;
212
        size_t offset = 1;
213
        while (!mark_bit) {
214
            if (len <= offset + payload_size) {
xiongziliang committed
215
                //FU-A end
216 217
                mark_bit = true;
                payload_size = len - offset;
218 219
                s_e_r_flags = (1 << 6) | nal_type;
            } else if (fu_a_start) {
xiongziliang committed
220
                //FU-A start
221
                s_e_r_flags = (1 << 7) | nal_type;
xiongziliang committed
222
            } else {
xiongziliang committed
223
                //FU-A mid
224
                s_e_r_flags = nal_type;
xiongziliang committed
225
            }
226 227 228

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

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

254
}//namespace mediakit