H264.h 12.2 KB
Newer Older
1
/*
2 3
 * MIT License
 *
xiongziliang committed
4
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
xiongziliang committed
26

27 28
#ifndef ZLMEDIAKIT_H264_H
#define ZLMEDIAKIT_H264_H
xiongziliang committed
29 30

#include "Frame.h"
31
#include "Track.h"
32 33
#include "RtspMuxer/RtspSdp.h"

xiongziliang committed
34

xiongziliang committed
35 36
#define H264_TYPE(v) ((uint8_t)(v) & 0x1F)

xiongziliang committed
37
namespace mediakit{
xiongziliang committed
38

39 40
bool getAVCInfo(const string &strSps,int &iVideoWidth, int &iVideoHeight, float  &iVideoFps);
bool getAVCInfo(const char * sps,int sps_len,int &iVideoWidth, int &iVideoHeight, float  &iVideoFps);
41
void splitH264(const char *ptr, int len, const std::function<void(const char *, int)> &cb);
xiongziliang committed
42

xiongziliang committed
43
/**
44
 * 264帧类
xiongziliang committed
45
 */
46
class H264Frame : public Frame {
xiongziliang committed
47
public:
48
    typedef std::shared_ptr<H264Frame> Ptr;
xiongziliang committed
49

xiongziliang committed
50 51 52 53 54 55 56
    typedef enum {
        NAL_SPS = 7,
        NAL_PPS = 8,
        NAL_IDR = 5,
        NAL_B_P = 1
    } NalType;

57 58 59 60 61 62
    char *data() const override{
        return (char *)buffer.data();
    }
    uint32_t size() const override {
        return buffer.size();
    }
xiongziliang committed
63
    uint32_t dts() const override {
64 65
        return timeStamp;
    }
xiongziliang committed
66 67 68 69 70

    uint32_t pts() const override {
        return ptsStamp ? ptsStamp : timeStamp;
    }

71 72 73
    uint32_t prefixSize() const override{
        return iPrefixSize;
    }
74

75 76 77
    TrackType getTrackType() const override{
        return TrackVideo;
    }
78

79 80 81
    CodecId getCodecId() const override{
        return CodecH264;
    }
82

83
    bool keyFrame() const override {
xiongziliang committed
84
        return type == NAL_IDR;
85 86 87 88
    }
public:
    uint16_t sequence;
    uint32_t timeStamp;
xiongziliang committed
89
    uint32_t ptsStamp = 0;
90 91 92
    unsigned char type;
    string buffer;
    uint32_t iPrefixSize = 4;
xiongziliang committed
93 94
};

95 96

class H264FrameNoCopyAble : public FrameNoCopyAble {
xiongziliang committed
97
public:
98
    typedef std::shared_ptr<H264FrameNoCopyAble> Ptr;
xiongziliang committed
99

100 101 102 103 104 105
    H264FrameNoCopyAble(char *ptr,uint32_t size,uint32_t stamp,int prefixeSize = 4){
        buffer_ptr = ptr;
        buffer_size = size;
        timeStamp = stamp;
        iPrefixSize = prefixeSize;
    }
106

107 108 109
    TrackType getTrackType() const override{
        return TrackVideo;
    }
110

111 112 113
    CodecId getCodecId() const override{
        return CodecH264;
    }
114

115
    bool keyFrame() const override {
xiongziliang committed
116
        return H264_TYPE(buffer_ptr[iPrefixSize]) == H264Frame::NAL_IDR;
117
    }
xiongziliang committed
118 119
};

120 121 122 123 124 125 126 127 128 129 130 131 132 133
class H264FrameSubFrame : public H264FrameNoCopyAble{
public:
    typedef std::shared_ptr<H264FrameSubFrame> Ptr;

    H264FrameSubFrame(const Frame::Ptr &strongRef,
                           char *ptr,
                           uint32_t size,
                           uint32_t stamp,
                           int prefixeSize) : H264FrameNoCopyAble(ptr,size,stamp,prefixeSize){
        _strongRef = strongRef;
    }
private:
    Frame::Ptr _strongRef;
};
134

xiongziliang committed
135 136 137
/**
 * 264视频通道
 */
xiongziliang committed
138
class H264Track : public VideoTrack{
xiongziliang committed
139
public:
xiongziliang committed
140
    typedef std::shared_ptr<H264Track> Ptr;
141 142

    /**
xiongziliang committed
143 144 145 146 147
     * 不指定sps pps构造h264类型的媒体
     * 在随后的inputFrame中获取sps pps
     */
    H264Track(){}
    /**
148 149 150 151 152 153 154 155 156
     * 构造h264类型的媒体
     * @param sps sps帧数据
     * @param pps pps帧数据
     * @param sps_prefix_len 264头长度,可以为3个或4个字节,一般为0x00 00 00 01
     * @param pps_prefix_len 264头长度,可以为3个或4个字节,一般为0x00 00 00 01
     */
    H264Track(const string &sps,const string &pps,int sps_prefix_len = 4,int pps_prefix_len = 4){
        _sps = sps.substr(sps_prefix_len);
        _pps = pps.substr(pps_prefix_len);
xiongziliang committed
157
        onReady();
158 159 160 161 162 163 164 165 166 167 168 169 170
    }

    /**
     * 构造h264类型的媒体
     * @param sps sps帧
     * @param pps pps帧
     */
    H264Track(const Frame::Ptr &sps,const Frame::Ptr &pps){
        if(sps->getCodecId() != CodecH264 || pps->getCodecId() != CodecH264 ){
            throw std::invalid_argument("必须输入H264类型的帧");
        }
        _sps = string(sps->data() + sps->prefixSize(),sps->size() - sps->prefixSize());
        _pps = string(pps->data() + pps->prefixSize(),pps->size() - pps->prefixSize());
xiongziliang committed
171
        onReady();
xiongziliang committed
172
    }
173 174 175 176 177

    /**
     * 返回不带0x00 00 00 01头的sps
     * @return
     */
xiongziliang committed
178 179 180
    const string &getSps() const{
        return _sps;
    }
181 182 183 184 185

    /**
     * 返回不带0x00 00 00 01头的pps
     * @return
     */
xiongziliang committed
186 187 188
    const string &getPps() const{
        return _pps;
    }
189 190

    CodecId getCodecId() const override {
191
        return CodecH264;
xiongziliang committed
192
    }
193 194 195 196 197 198

    /**
     * 返回视频高度
     * @return
     */
    int getVideoHeight() const override{
199
        return _height ;
200 201 202 203 204 205 206
    }

    /**
     * 返回视频宽度
     * @return
     */
    int getVideoWidth() const override{
207
        return _width;
208 209 210 211 212 213 214 215 216
    }

    /**
     * 返回视频fps
     * @return
     */
    float getVideoFps() const override{
        return _fps;
    }
xiongziliang committed
217

218 219
    bool ready() override {
        return !_sps.empty() && !_pps.empty();
xiongziliang committed
220 221
    }

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    /**
    * 输入数据帧,并获取sps pps
    * @param frame 数据帧
    */
    void inputFrame(const Frame::Ptr &frame) override{
        int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
        if(type == H264Frame::NAL_SPS){
            //有些设备会把SPS PPS IDR帧当做一个帧打包,所以我们要split一下
            bool  first_frame = true;
            splitH264(frame->data() + frame->prefixSize(),
                      frame->size() - frame->prefixSize(),
                      [&](const char *ptr, int len){
                      if(first_frame){
                          H264FrameSubFrame::Ptr sub_frame = std::make_shared<H264FrameSubFrame>(frame,
                                                                                                 frame->data(),
                                                                                                 len + frame->prefixSize(),
                                                                                                 frame->stamp(),
                                                                                                 frame->prefixSize());
                          inputFrame_l(sub_frame);
                          first_frame = false;
                      }else{
                          H264FrameSubFrame::Ptr sub_frame = std::make_shared<H264FrameSubFrame>(frame,
                                                                                                 (char *)ptr,
                                                                                                 len ,
                                                                                                 frame->stamp(),
                                                                                                 3);
                          inputFrame_l(sub_frame);
                      }
            });
        } else{
            inputFrame_l(frame);
        }
    }
private:
    /**
     * 解析sps获取宽高fps
     */
    void onReady(){
        getAVCInfo(_sps,_width,_height,_fps);
    }
    Track::Ptr clone() override {
        return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
    }
xiongziliang committed
265 266 267 268 269

    /**
     * 输入数据帧,并获取sps pps
     * @param frame 数据帧
     */
270
    void inputFrame_l(const Frame::Ptr &frame){
xiongziliang committed
271
        int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
xiongziliang committed
272
        switch (type){
xiongziliang committed
273
            case H264Frame::NAL_SPS:{
xiongziliang committed
274 275 276 277
                //sps
                _sps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
            }
                break;
xiongziliang committed
278
            case H264Frame::NAL_PPS:{
xiongziliang committed
279 280 281 282 283
                //pps
                _pps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
            }
                break;

xiongziliang committed
284
            case H264Frame::NAL_IDR:{
xiongziliang committed
285
                //I
xiongziliang committed
286
                insertConfigFrame(frame);
287
                VideoTrack::inputFrame(frame);
xiongziliang committed
288
                _last_frame_is_idr = true;
xiongziliang committed
289 290 291
            }
                break;

xiongziliang committed
292
            case H264Frame::NAL_B_P:{
xiongziliang committed
293
                //B or P
294
                VideoTrack::inputFrame(frame);
xiongziliang committed
295
                _last_frame_is_idr = false;
xiongziliang committed
296 297 298
            }
                break;
        }
xiongziliang committed
299 300 301 302

        if(_width == 0 && ready()){
            onReady();
        }
xiongziliang committed
303
    }
xiongziliang committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317

private:
    //在idr帧前插入sps pps帧
    void insertConfigFrame(const Frame::Ptr &frame){
        if(_last_frame_is_idr){
            return;
        }

        if(!_sps.empty()){
            if(!_spsFrame){
                _spsFrame = std::make_shared<H264Frame>();
                _spsFrame->type = H264Frame::NAL_SPS;
                _spsFrame->iPrefixSize = 4;
            }
318 319
            _spsFrame->buffer.assign("\x0\x0\x0\x1",4);
            _spsFrame->buffer.append(_sps);
xiongziliang committed
320 321 322 323 324 325 326 327 328 329
            _spsFrame->timeStamp = frame->stamp();
            VideoTrack::inputFrame(_spsFrame);
        }

        if(!_pps.empty()){
            if(!_ppsFrame) {
                _ppsFrame = std::make_shared<H264Frame>();
                _ppsFrame->type = H264Frame::NAL_PPS;
                _ppsFrame->iPrefixSize = 4;
            }
330 331
            _ppsFrame->buffer.assign("\x0\x0\x0\x1",4);
            _ppsFrame->buffer.append(_pps);
xiongziliang committed
332 333 334 335
            _ppsFrame->timeStamp = frame->stamp();
            VideoTrack::inputFrame(_ppsFrame);
        }
    }
336
private:
xiongziliang committed
337 338
    string _sps;
    string _pps;
339 340 341
    int _width = 0;
    int _height = 0;
    float _fps = 0;
xiongziliang committed
342
    bool _last_frame_is_idr = false;
343 344
    H264Frame::Ptr _spsFrame;
    H264Frame::Ptr _ppsFrame;
xiongziliang committed
345 346 347
};


348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
/**
* h264类型sdp
*/
class H264Sdp : public Sdp {
public:

    /**
     *
     * @param sps 264 sps,不带0x00000001头
     * @param pps 264 pps,不带0x00000001头
     * @param playload_type  rtp playload type 默认96
     * @param bitrate 比特率
     */
    H264Sdp(const string &strSPS,
            const string &strPPS,
            int playload_type = 96,
            int bitrate = 4000) : Sdp(90000,playload_type) {
        //视频通道
        _printer << "m=video 0 RTP/AVP " << playload_type << "\r\n";
        _printer << "b=AS:" << bitrate << "\r\n";
        _printer << "a=rtpmap:" << playload_type << " H264/" << 90000 << "\r\n";
        _printer << "a=fmtp:" << playload_type << " packetization-mode=1;profile-level-id=";

        char strTemp[100];
        uint32_t profile_level_id = 0;
        if (strSPS.length() >= 4) { // sanity check
            profile_level_id = (uint8_t(strSPS[1]) << 16) |
                               (uint8_t(strSPS[2]) << 8)  |
                               (uint8_t(strSPS[3])); // profile_idc|constraint_setN_flag|level_idc
        }
        memset(strTemp, 0, 100);
        sprintf(strTemp, "%06X", profile_level_id);
        _printer << strTemp;
        _printer << ";sprop-parameter-sets=";
        memset(strTemp, 0, 100);
        av_base64_encode(strTemp, 100, (uint8_t *) strSPS.data(), strSPS.size());
        _printer << strTemp << ",";
        memset(strTemp, 0, 100);
        av_base64_encode(strTemp, 100, (uint8_t *) strPPS.data(), strPPS.size());
        _printer << strTemp << "\r\n";
        _printer << "a=control:trackID=" << getTrackType() << "\r\n";
    }

    string getSdp() const override {
        return _printer;
    }

    TrackType getTrackType() const override {
        return TrackVideo;
    }

    CodecId getCodecId() const override {
        return CodecH264;
    }
private:
    _StrPrinter _printer;
};




xiongziliang committed
409
}//namespace mediakit
xiongziliang committed
410

411 412

#endif //ZLMEDIAKIT_H264_H