H264.h 12.8 KB
Newer Older
xiongziliang committed
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"
xiongziliang committed
32 33
#include "Util/base64.h"
using namespace toolkit;
xiongziliang committed
34 35
#define H264_TYPE(v) ((uint8_t)(v) & 0x1F)

xiongziliang committed
36
namespace mediakit{
xiongziliang committed
37

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

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

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

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

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

69 70 71
    uint32_t prefixSize() const override{
        return iPrefixSize;
    }
72

73 74 75
    TrackType getTrackType() const override{
        return TrackVideo;
    }
76

77 78 79
    CodecId getCodecId() const override{
        return CodecH264;
    }
80

81
    bool keyFrame() const override {
82
        return H264_TYPE(buffer[iPrefixSize]) == H264Frame::NAL_IDR;
83
    }
xiongziliang committed
84 85 86 87 88 89 90 91 92 93

    bool configFrame() const override{
        switch(H264_TYPE(buffer[iPrefixSize]) ){
            case H264Frame::NAL_SPS:
            case H264Frame::NAL_PPS:
                return true;
            default:
                return false;
        }
    }
94 95
public:
    uint32_t timeStamp;
xiongziliang committed
96
    uint32_t ptsStamp = 0;
97 98
    string buffer;
    uint32_t iPrefixSize = 4;
xiongziliang committed
99 100
};

101

102 103 104 105 106
/**
 * 防止内存拷贝的H264类
 * 用户可以通过该类型快速把一个指针无拷贝的包装成Frame类
 * 该类型在DevChannel中有使用
 */
107
class H264FrameNoCacheAble : public FrameNoCacheAble {
xiongziliang committed
108
public:
109
    typedef std::shared_ptr<H264FrameNoCacheAble> Ptr;
xiongziliang committed
110

111
    H264FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts , uint32_t pts ,int prefixeSize = 4){
xiongziliang committed
112 113 114 115 116
        _ptr = ptr;
        _size = size;
        _dts = dts;
        _pts = pts;
        _prefixSize = prefixeSize;
117
    }
118

119 120 121
    TrackType getTrackType() const override{
        return TrackVideo;
    }
122

123 124 125
    CodecId getCodecId() const override{
        return CodecH264;
    }
126

127
    bool keyFrame() const override {
xiongziliang committed
128
        return H264_TYPE(_ptr[_prefixSize]) == H264Frame::NAL_IDR;
129
    }
xiongziliang committed
130 131 132 133 134 135 136 137 138 139

    bool configFrame() const override{
        switch(H264_TYPE(_ptr[_prefixSize])){
            case H264Frame::NAL_SPS:
            case H264Frame::NAL_PPS:
                return true;
            default:
                return false;
        }
    }
xiongziliang committed
140 141
};

142 143 144 145 146 147
/**
 * 一个H264Frame类中可以有多个帧,他们通过 0x 00 00 01 分隔
 * ZLMediaKit会先把这种复合帧split成单个帧然后再处理
 * 一个复合帧可以通过无内存拷贝的方式切割成多个H264FrameSubFrame
 * 提供该类的目的是切换复合帧时防止内存拷贝,提高性能
 */
148 149
template<typename Parent>
class FrameInternal : public Parent{
150
public:
151 152 153 154 155
    typedef std::shared_ptr<FrameInternal> Ptr;
    FrameInternal(const Frame::Ptr &parent_frame,
                  char *ptr,
                  uint32_t size,
                  int prefixeSize) : Parent(ptr,size,parent_frame->dts(),parent_frame->pts(),prefixeSize){
156 157 158 159
        _parent_frame = parent_frame;
    }
    bool cacheAble() const override {
        return _parent_frame->cacheAble();
160 161
    }
private:
162
    Frame::Ptr _parent_frame;
163
};
164

165 166
typedef FrameInternal<H264FrameNoCacheAble> H264FrameInternal;

xiongziliang committed
167 168 169
/**
 * 264视频通道
 */
xiongziliang committed
170
class H264Track : public VideoTrack{
xiongziliang committed
171
public:
xiongziliang committed
172
    typedef std::shared_ptr<H264Track> Ptr;
173 174

    /**
xiongziliang committed
175 176 177 178 179
     * 不指定sps pps构造h264类型的媒体
     * 在随后的inputFrame中获取sps pps
     */
    H264Track(){}
    /**
180 181 182 183 184 185 186 187 188
     * 构造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
189
        onReady();
190 191 192 193 194 195 196 197 198 199 200 201 202
    }

    /**
     * 构造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
203
        onReady();
xiongziliang committed
204
    }
205 206 207 208 209

    /**
     * 返回不带0x00 00 00 01头的sps
     * @return
     */
xiongziliang committed
210 211 212
    const string &getSps() const{
        return _sps;
    }
213 214 215 216 217

    /**
     * 返回不带0x00 00 00 01头的pps
     * @return
     */
xiongziliang committed
218 219 220
    const string &getPps() const{
        return _pps;
    }
221 222

    CodecId getCodecId() const override {
223
        return CodecH264;
xiongziliang committed
224
    }
225 226 227 228 229 230

    /**
     * 返回视频高度
     * @return
     */
    int getVideoHeight() const override{
231
        return _height ;
232 233 234 235 236 237 238
    }

    /**
     * 返回视频宽度
     * @return
     */
    int getVideoWidth() const override{
239
        return _width;
240 241 242 243 244 245 246 247 248
    }

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

250 251
    bool ready() override {
        return !_sps.empty() && !_pps.empty();
xiongziliang committed
252 253
    }

254 255 256 257 258 259 260 261 262 263 264 265
    /**
    * 输入数据帧,并获取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){
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
                          if(first_frame){
                              H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame,
                                                                                                     frame->data(),
                                                                                                     len + frame->prefixSize(),
                                                                                                     frame->prefixSize());
                              inputFrame_l(sub_frame);
                              first_frame = false;
                          }else{
                              H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame,
                                                                                                     (char *)ptr,
                                                                                                     len ,
                                                                                                     3);
                              inputFrame_l(sub_frame);
                          }
                      });
281 282 283 284 285 286 287 288 289 290 291 292 293 294
        } 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
295 296 297 298 299

    /**
     * 输入数据帧,并获取sps pps
     * @param frame 数据帧
     */
300
    void inputFrame_l(const Frame::Ptr &frame){
xiongziliang committed
301
        int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
xiongziliang committed
302
        switch (type){
xiongziliang committed
303
            case H264Frame::NAL_SPS:{
xiongziliang committed
304 305 306 307
                //sps
                _sps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
            }
                break;
xiongziliang committed
308
            case H264Frame::NAL_PPS:{
xiongziliang committed
309 310 311 312 313
                //pps
                _pps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
            }
                break;

xiongziliang committed
314
            case H264Frame::NAL_IDR:{
xiongziliang committed
315
                //I
xiongziliang committed
316
                insertConfigFrame(frame);
317
                VideoTrack::inputFrame(frame);
xiongziliang committed
318
                _last_frame_is_idr = true;
xiongziliang committed
319 320 321
            }
                break;

xiongziliang committed
322
            case H264Frame::NAL_B_P:{
xiongziliang committed
323
                //B or P
324
                VideoTrack::inputFrame(frame);
xiongziliang committed
325
                _last_frame_is_idr = false;
xiongziliang committed
326 327 328
            }
                break;
        }
xiongziliang committed
329 330 331 332

        if(_width == 0 && ready()){
            onReady();
        }
xiongziliang committed
333
    }
xiongziliang committed
334

xiongziliang committed
335 336
    //生成sdp
    Sdp::Ptr getSdp() override ;
xiongziliang committed
337 338 339 340 341 342 343 344
private:
    //在idr帧前插入sps pps帧
    void insertConfigFrame(const Frame::Ptr &frame){
        if(_last_frame_is_idr){
            return;
        }

        if(!_sps.empty()){
xiongziliang committed
345 346 347 348 349 350
            auto spsFrame = std::make_shared<H264Frame>();
            spsFrame->iPrefixSize = 4;
            spsFrame->buffer.assign("\x0\x0\x0\x1",4);
            spsFrame->buffer.append(_sps);
            spsFrame->timeStamp = frame->stamp();
            VideoTrack::inputFrame(spsFrame);
xiongziliang committed
351 352 353
        }

        if(!_pps.empty()){
xiongziliang committed
354 355 356 357 358 359
            auto ppsFrame = std::make_shared<H264Frame>();
            ppsFrame->iPrefixSize = 4;
            ppsFrame->buffer.assign("\x0\x0\x0\x1",4);
            ppsFrame->buffer.append(_pps);
            ppsFrame->timeStamp = frame->stamp();
            VideoTrack::inputFrame(ppsFrame);
xiongziliang committed
360 361
        }
    }
362
private:
xiongziliang committed
363 364
    string _sps;
    string _pps;
365 366 367
    int _width = 0;
    int _height = 0;
    float _fps = 0;
xiongziliang committed
368
    bool _last_frame_is_idr = false;
xiongziliang committed
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 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
/**
* 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
431
}//namespace mediakit
xiongziliang committed
432

433 434

#endif //ZLMEDIAKIT_H264_H