RtspMediaSource.h 6.02 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
xiongziliang committed
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.
xzl committed
9 10 11 12 13
 */

#ifndef SRC_RTSP_RTSPMEDIASOURCE_H_
#define SRC_RTSP_RTSPMEDIASOURCE_H_

xiongzilaing committed
14
#include <mutex>
xzl committed
15 16
#include <string>
#include <memory>
xiongzilaing committed
17
#include <functional>
xzl committed
18
#include <unordered_map>
xiongziliang committed
19
#include "Common/config.h"
20
#include "Common/MediaSource.h"
xiongziliang committed
21
#include "RtpCodec.h"
xzl committed
22
#include "Util/logger.h"
xiongzilaing committed
23
#include "Util/RingBuffer.h"
xzl committed
24
#include "Util/TimeTicker.h"
xiongzilaing committed
25 26 27
#include "Util/ResourcePool.h"
#include "Util/NoticeCenter.h"
#include "Thread/ThreadPool.h"
xzl committed
28
using namespace std;
xiongziliang committed
29
using namespace toolkit;
30 31
#define RTP_GOP_SIZE 512
namespace mediakit {
xzl committed
32

33
/**
34 35 36 37 38
 * rtsp媒体源的数据抽象
 * rtsp有关键的两要素,分别是sdp、rtp包
 * 只要生成了这两要素,那么要实现rtsp推流、rtsp服务器就很简单了
 * rtsp推拉流协议中,先传递sdp,然后再协商传输方式(tcp/udp/组播),最后一直传递rtp
 */
39
class RtspMediaSource : public MediaSource, public RingDelegate<RtpPacket::Ptr>, public PacketCache<RtpPacket> {
xzl committed
40
public:
41 42
    typedef ResourcePool<RtpPacket> PoolType;
    typedef std::shared_ptr<RtspMediaSource> Ptr;
43 44
    typedef std::shared_ptr<List<RtpPacket::Ptr> > RingDataType;
    typedef RingBuffer<RingDataType> RingType;
45 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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 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

    /**
     * 构造函数
     * @param vhost 虚拟主机名
     * @param app 应用名
     * @param stream_id 流id
     * @param ring_size 可以设置固定的环形缓冲大小,0则自适应
     */
    RtspMediaSource(const string &vhost,
                    const string &app,
                    const string &stream_id,
                    int ring_size = RTP_GOP_SIZE) :
            MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}

    virtual ~RtspMediaSource() {}

    /**
     * 获取媒体源的环形缓冲
     */
    const RingType::Ptr &getRing() const {
        return _ring;
    }

    /**
     * 获取播放器个数
     */
    int readerCount() override {
        return _ring ? _ring->readerCount() : 0;
    }

    /**
     * 获取该源的sdp
     */
    const string &getSdp() const {
        return _sdp;
    }

    /**
     * 获取相应轨道的ssrc
     */
    virtual uint32_t getSsrc(TrackType trackType) {
        auto track = _sdp_parser.getTrack(trackType);
        if (!track) {
            return 0;
        }
        return track->_ssrc;
    }

    /**
     * 获取相应轨道的seqence
     */
    virtual uint16_t getSeqence(TrackType trackType) {
        auto track = _sdp_parser.getTrack(trackType);
        if (!track) {
            return 0;
        }
        return track->_seq;
    }

    /**
     * 获取相应轨道的时间戳,单位毫秒
     */
    uint32_t getTimeStamp(TrackType trackType) override {
        auto track = _sdp_parser.getTrack(trackType);
        if (track) {
            return track->_time_stamp;
        }
        auto tracks = _sdp_parser.getAvailableTrack();
        switch (tracks.size()) {
            case 0:
                return 0;
            case 1:
                return tracks[0]->_time_stamp;
            default:
xiongziliang committed
119
                return MIN(tracks[0]->_time_stamp, tracks[1]->_time_stamp);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
        }
    }

    /**
     * 更新时间戳
     */
     void setTimeStamp(uint32_t uiStamp) override {
        auto tracks = _sdp_parser.getAvailableTrack();
        for (auto &track : tracks) {
            track->_time_stamp = uiStamp;
        }
    }

    /**
     * 设置sdp
     */
    virtual void setSdp(const string &sdp) {
        _sdp = sdp;
        _sdp_parser.load(sdp);
        _have_video = (bool)_sdp_parser.getTrack(TrackVideo);
        if (_ring) {
            regist();
        }
    }

    /**
     * 输入rtp
     * @param rtp rtp包
     * @param keyPos 该包是否为关键帧的第一个包
     */
    void onWrite(const RtpPacket::Ptr &rtp, bool keyPos) override {
        auto track = _sdp_parser.getTrack(rtp->type);
        if (track) {
            track->_seq = rtp->sequence;
            track->_time_stamp = rtp->timeStamp;
            track->_ssrc = rtp->ssrc;
        }
        if (!_ring) {
            weak_ptr<RtspMediaSource> weakSelf = dynamic_pointer_cast<RtspMediaSource>(shared_from_this());
            auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) {
                auto strongSelf = weakSelf.lock();
                if (!strongSelf) {
                    return;
                }
                strongSelf->onReaderChanged(size);
            };
xiongziliang committed
166 167 168
            //rtp包缓存最大允许2048个,大概最多3MB数据
            //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍
            //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值
169 170 171 172 173 174
            _ring = std::make_shared<RingType>(_ring_size, std::move(lam));
            onReaderChanged(0);
            if (!_sdp.empty()) {
                regist();
            }
        }
175
        PacketCache<RtpPacket>::inputPacket(rtp->type == TrackVideo, rtp, keyPos);
176
    }
177

178
private:
179 180

    /**
181
     * 批量flush rtp包时触发该函数
182
     * @param rtp_list rtp包列表
183
     * @param key_pos 是否包含关键帧
184
     */
185 186 187
    void onFlush(std::shared_ptr<List<RtpPacket::Ptr> > &rtp_list, bool key_pos) override {
        //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存
        _ring->write(rtp_list, _have_video ? key_pos : true);
188 189
    }

190 191 192 193
    /**
     * 每次增减消费者都会触发该函数
     */
    void onReaderChanged(int size) {
194
        if (size == 0) {
195 196 197
            onNoneReader();
        }
    }
198
private:
199 200 201 202 203
    int _ring_size;
    bool _have_video = false;
    SdpParser _sdp_parser;
    string _sdp;
    RingType::Ptr _ring;
xzl committed
204 205
};

xiongziliang committed
206
} /* namespace mediakit */
xzl committed
207 208

#endif /* SRC_RTSP_RTSPMEDIASOURCE_H_ */