MediaSource.cpp 20.5 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 10 11
 */

#include "MediaSource.h"
xiongziliang committed
12
#include "Record/MP4Reader.h"
13
#include "Util/util.h"
14
#include "Network/sockutil.h"
15
#include "Network/TcpSession.h"
xiongziliang committed
16 17
using namespace toolkit;
namespace mediakit {
18

xiongziliang committed
19 20
recursive_mutex s_media_source_mtx;
MediaSource::SchemaVhostAppStreamMap s_media_source_map;
21

xiongziliang committed
22 23 24 25
MediaSource::MediaSource(const string &schema, const string &vhost, const string &app, const string &stream_id){
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    if (!enableVhost) {
        _vhost = DEFAULT_VHOST;
xiongziliang committed
26
    } else {
xiongziliang committed
27
        _vhost = vhost.empty() ? DEFAULT_VHOST : vhost;
xiongziliang committed
28
    }
xiongziliang committed
29 30 31
    _schema = schema;
    _app = app;
    _stream_id = stream_id;
xiongziliang committed
32 33 34 35 36 37 38
}

MediaSource::~MediaSource() {
    unregist();
}

const string& MediaSource::getSchema() const {
xiongziliang committed
39
    return _schema;
xiongziliang committed
40 41 42
}

const string& MediaSource::getVhost() const {
xiongziliang committed
43
    return _vhost;
xiongziliang committed
44 45 46 47
}

const string& MediaSource::getApp() const {
    //获取该源的id
xiongziliang committed
48
    return _app;
xiongziliang committed
49 50 51
}

const string& MediaSource::getId() const {
xiongziliang committed
52
    return _stream_id;
xiongziliang committed
53 54
}

xiongziliang committed
55
vector<Track::Ptr> MediaSource::getTracks(bool ready) const {
56 57 58
    auto listener = _listener.lock();
    if(!listener){
        return vector<Track::Ptr>();
xiongziliang committed
59
    }
60
    return listener->getTracks(const_cast<MediaSource &>(*this), ready);
xiongziliang committed
61 62 63 64 65 66 67 68 69 70
}

void MediaSource::setListener(const std::weak_ptr<MediaSourceEvent> &listener){
    _listener = listener;
}

const std::weak_ptr<MediaSourceEvent>& MediaSource::getListener() const{
    return _listener;
}

71 72 73 74 75 76 77
int MediaSource::totalReaderCount(){
    auto listener = _listener.lock();
    if(!listener){
        return readerCount();
    }
    return listener->totalReaderCount(*this);
}
xiongziliang committed
78 79

bool MediaSource::seekTo(uint32_t stamp) {
xiongziliang committed
80 81 82 83
    auto listener = _listener.lock();
    if(!listener){
        return false;
    }
xiongziliang committed
84
    return listener->seekTo(*this, stamp);
xiongziliang committed
85 86 87 88 89 90 91 92 93 94
}

bool MediaSource::close(bool force) {
    auto listener = _listener.lock();
    if(!listener){
        return false;
    }
    return listener->close(*this,force);
}

95
void MediaSource::onReaderChanged(int size) {
xiongziliang committed
96
    auto listener = _listener.lock();
97 98
    if (listener) {
        listener->onReaderChanged(*this, size);
99
    }
xiongziliang committed
100 101
}

102 103 104
bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path){
    auto listener = _listener.lock();
    if (!listener) {
105
        WarnL << "未设置MediaSource的事件监听者,setupRecord失败:" << getSchema() << "/" << getVhost() << "/" << getApp() << "/" << getId();
106 107 108 109 110 111 112 113 114 115 116 117 118
        return false;
    }
    return listener->setupRecord(*this, type, start, custom_path);
}

bool MediaSource::isRecording(Recorder::type type){
    auto listener = _listener.lock();
    if(!listener){
        return false;
    }
    return listener->isRecording(*this, type);
}

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
void MediaSource::startSendRtp(const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb){
    auto listener = _listener.lock();
    if (!listener) {
        cb(SockException(Err_other, "尚未设置事件监听器"));
        return;
    }
    return listener->startSendRtp(*this, dst_url, dst_port, ssrc, is_udp, cb);
}

bool MediaSource::stopSendRtp() {
    auto listener = _listener.lock();
    if (!listener) {
        return false;
    }
    return listener->stopSendRtp(*this);
}

xiongziliang committed
136
void MediaSource::for_each_media(const function<void(const MediaSource::Ptr &src)> &cb) {
xiongziliang committed
137
    decltype(s_media_source_map) copy;
xiongziliang committed
138
    {
xiongziliang committed
139
        //拷贝s_media_source_map后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码
xiongziliang committed
140
        //很容易导致多个锁交叉死锁的情况,而且该函数使用频率不高,拷贝开销相对来说是可以接受的
xiongziliang committed
141 142
        lock_guard<recursive_mutex> lock(s_media_source_mtx);
        copy = s_media_source_map;
xiongziliang committed
143 144 145
    }

    for (auto &pr0 : copy) {
xiongziliang committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159
        for (auto &pr1 : pr0.second) {
            for (auto &pr2 : pr1.second) {
                for (auto &pr3 : pr2.second) {
                    auto src = pr3.second.lock();
                    if(src){
                        cb(src);
                    }
                }
            }
        }
    }
}

template<typename MAP, typename FUNC>
160
static bool searchMedia(MAP &map, const string &schema, const string &vhost, const string &app, const string &id, FUNC &&func) {
xiongziliang committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    auto it0 = map.find(schema);
    if (it0 == map.end()) {
        //未找到协议
        return false;
    }
    auto it1 = it0->second.find(vhost);
    if (it1 == it0->second.end()) {
        //未找到vhost
        return false;
    }
    auto it2 = it1->second.find(app);
    if (it2 == it1->second.end()) {
        //未找到app
        return false;
    }
    auto it3 = it2->second.find(id);
    if (it3 == it2->second.end()) {
        //未找到streamId
        return false;
    }
    return func(it0, it1, it2, it3);
}
183

xiongziliang committed
184 185 186 187 188 189 190 191 192 193 194
template<typename MAP, typename IT0, typename IT1, typename IT2>
static void eraseIfEmpty(MAP &map, IT0 it0, IT1 it1, IT2 it2) {
    if (it2->second.empty()) {
        it1->second.erase(it2);
        if (it1->second.empty()) {
            it0->second.erase(it1);
            if (it0->second.empty()) {
                map.erase(it0);
            }
        }
    }
195
}
xiongziliang committed
196

xiongziliang committed
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, const string &app, const string &id, bool create_new) {
    string vhost = vhost_in;
    GET_CONFIG(bool,enableVhost,General::kEnableVhost);
    if(vhost.empty() || !enableVhost){
        vhost = DEFAULT_VHOST;
    }

    MediaSource::Ptr ret;
    {
        lock_guard<recursive_mutex> lock(s_media_source_mtx);
        //查找某一媒体源,找到后返回
        searchMedia(s_media_source_map, schema, vhost, app, id,
                    [&](MediaSource::SchemaVhostAppStreamMap::iterator &it0, MediaSource::VhostAppStreamMap::iterator &it1,
                        MediaSource::AppStreamMap::iterator &it2, MediaSource::StreamMap::iterator &it3) {
            ret = it3->second.lock();
            if (!ret) {
                //该对象已经销毁
                it2->second.erase(it3);
                eraseIfEmpty(s_media_source_map, it0, it1, it2);
                return false;
            }
            return true;
        });
    }

    if(!ret && create_new){
        //未查找媒体源,则创建一个
        ret = MediaSource::createFromMP4(schema, vhost, app, id);
    }
    return ret;
}

static void findAsync_l(const MediaInfo &info, const std::shared_ptr<TcpSession> &session, bool retry,
                        const function<void(const MediaSource::Ptr &src)> &cb){
    auto src = find_l(info._schema, info._vhost, info._app, info._streamid, true);
232
    if (src || !retry) {
233 234 235 236 237
        cb(src);
        return;
    }

    void *listener_tag = session.get();
xiongziliang committed
238
    weak_ptr<TcpSession> weak_session = session;
239

240
    GET_CONFIG(int, maxWaitMS, General::kMaxStreamWaitTimeMS);
xiongziliang committed
241
    auto on_timeout = session->getPoller()->doDelayTask(maxWaitMS, [cb, listener_tag]() {
242 243
        //最多等待一定时间,如果这个时间内,流未注册上,那么返回未找到流
        NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
244 245 246 247
        cb(nullptr);
        return 0;
    });

xiongziliang committed
248
    auto cancel_all = [on_timeout, listener_tag]() {
249
        //取消延时任务,防止多次回调
xiongziliang committed
250
        on_timeout->cancel();
251 252 253 254
        //取消媒体注册事件监听
        NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
    };

xiongziliang committed
255 256
    function<void()> close_player = [cb, cancel_all]() {
        cancel_all();
257 258 259 260
        //告诉播放器,流不存在,这样会立即断开播放器
        cb(nullptr);
    };

xiongziliang committed
261 262 263
    auto on_regist = [weak_session, info, cb, cancel_all](BroadcastMediaChangedArgs) {
        auto strong_session = weak_session.lock();
        if (!strong_session) {
264
            //自己已经销毁
xiongziliang committed
265
            cancel_all();
266 267 268
            return;
        }

xiongziliang committed
269 270 271 272 273
        if (!bRegist ||
            sender.getSchema() != info._schema ||
            sender.getVhost() != info._vhost ||
            sender.getApp() != info._app ||
            sender.getId() != info._streamid) {
274 275 276 277
            //不是自己感兴趣的事件,忽略之
            return;
        }

xiongziliang committed
278
        cancel_all();
279

280
        //播发器请求的流终于注册上了,切换到自己的线程再回复
xiongziliang committed
281 282
        strong_session->async([weak_session, info, cb]() {
            auto strongSession = weak_session.lock();
283
            if (!strongSession) {
284 285 286 287
                return;
            }
            DebugL << "收到媒体注册事件,回复播放器:" << info._schema << "/" << info._vhost << "/" << info._app << "/" << info._streamid;
            //再找一遍媒体源,一般能找到
288
            findAsync_l(info, strongSession, false, cb);
289 290
        }, false);
    };
291

292
    //监听媒体注册事件
xiongziliang committed
293
    NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, on_regist);
294
    //广播未找到流,此时可以立即去拉流,这样还来得及
xiongziliang committed
295
    NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastNotFoundStream, info, static_cast<SockInfo &>(*session), close_player);
296
}
xiongziliang committed
297 298 299 300 301

void MediaSource::findAsync(const MediaInfo &info, const std::shared_ptr<TcpSession> &session,const function<void(const Ptr &src)> &cb){
    return findAsync_l(info, session, true, cb);
}

302 303 304 305
MediaSource::Ptr MediaSource::find(const string &schema, const string &vhost, const string &app, const string &id) {
    return find_l(schema, vhost, app, id, false);
}

306 307 308 309 310 311 312 313 314 315 316 317
MediaSource::Ptr MediaSource::find(const string &vhost, const string &app, const string &stream_id){
    auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id);
    if (src) {
        return src;
    }
    src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id);
    if (src) {
        return src;
    }
    return MediaSource::find(HLS_SCHEMA, vhost, app, stream_id);
}

xiongziliang committed
318
void MediaSource::emitEvent(bool regist){
319 320
    auto listener = _listener.lock();
    if (listener) {
xiongziliang committed
321 322 323 324 325
        //触发回调
        listener->onRegist(*this, regist);
    }
    //触发广播
    NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, regist, *this);
326
    InfoL << (regist ? "媒体注册:" : "媒体注销:") << _schema << " " << _vhost << " " << _app << " " << _stream_id;
xiongziliang committed
327 328 329 330 331 332 333
}

void MediaSource::regist() {
    {
        //减小互斥锁临界区
        lock_guard<recursive_mutex> lock(s_media_source_mtx);
        s_media_source_map[_schema][_vhost][_app][_stream_id] = shared_from_this();
334
    }
xiongziliang committed
335
    emitEvent(true);
336
}
xiongziliang committed
337 338

//反注册该源
339
bool MediaSource::unregist() {
xiongziliang committed
340 341
    bool ret;
    {
xiongziliang committed
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
        //减小互斥锁临界区
        lock_guard<recursive_mutex> lock(s_media_source_mtx);
        ret = searchMedia(s_media_source_map, _schema, _vhost, _app, _stream_id,
                          [&](SchemaVhostAppStreamMap::iterator &it0, VhostAppStreamMap::iterator &it1,
                              AppStreamMap::iterator &it2, StreamMap::iterator &it3) {
          auto strong_self = it3->second.lock();
          if (strong_self && this != strong_self.get()) {
              //不是自己,不允许反注册
              return false;
          }
          it2->second.erase(it3);
          eraseIfEmpty(s_media_source_map, it0, it1, it2);
          return true;
      });
    }

    if (ret) {
        emitEvent(false);
xiongziliang committed
360 361 362
    }
    return ret;
}
xiongziliang committed
363 364 365

/////////////////////////////////////MediaInfo//////////////////////////////////////

366 367 368
void MediaInfo::parse(const string &url){
    //string url = "rtsp://127.0.0.1:8554/live/id?key=val&a=1&&b=2&vhost=vhost.com";
    auto schema_pos = url.find("://");
xiongziliang committed
369 370 371
    if (schema_pos != string::npos) {
        _schema = url.substr(0, schema_pos);
    } else {
372 373
        schema_pos = -3;
    }
xiongziliang committed
374 375
    auto split_vec = split(url.substr(schema_pos + 3), "/");
    if (split_vec.size() > 0) {
376 377
        auto vhost = split_vec[0];
        auto pos = vhost.find(":");
xiongziliang committed
378 379
        if (pos != string::npos) {
            _host = _vhost = vhost.substr(0, pos);
380
            _port = vhost.substr(pos + 1);
xiongziliang committed
381
        } else {
382
            _host = _vhost = vhost;
383
        }
384

xiongziliang committed
385
        if (_vhost == "localhost" || INADDR_NONE != inet_addr(_vhost.data())) {
386 387 388 389
            //如果访问的是localhost或ip,那么则为默认虚拟主机
            _vhost = DEFAULT_VHOST;
        }

390
    }
xiongziliang committed
391
    if (split_vec.size() > 1) {
392
        _app = split_vec[1];
393
    }
xiongziliang committed
394 395 396 397
    if (split_vec.size() > 2) {
        string stream_id;
        for (int i = 2; i < split_vec.size(); ++i) {
            stream_id.append(split_vec[i] + "/");
398
        }
xiongziliang committed
399 400
        if (stream_id.back() == '/') {
            stream_id.pop_back();
401
        }
xiongziliang committed
402 403 404 405
        auto pos = stream_id.find("?");
        if (pos != string::npos) {
            _streamid = stream_id.substr(0, pos);
            _param_strs = stream_id.substr(pos + 1);
406
            auto params = Parser::parseArgs(_param_strs);
xiongziliang committed
407
            if (params.find(VHOST_KEY) != params.end()) {
408
                _vhost = params[VHOST_KEY];
409
            }
xiongziliang committed
410 411
        } else {
            _streamid = stream_id;
412 413
        }
    }
414

xiongziliang committed
415 416
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    if (!enableVhost || _vhost.empty()) {
417
        //如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认
418
        _vhost = DEFAULT_VHOST;
419 420 421
    }
}

xiongziliang committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &file_path , bool check_app){
    GET_CONFIG(string, appName, Record::kAppName);
    if (check_app && app != appName) {
        return nullptr;
    }
#ifdef ENABLE_MP4
    try {
        MP4Reader::Ptr pReader(new MP4Reader(vhost, app, stream, file_path));
        pReader->startReadMP4();
        return MediaSource::find(schema, vhost, app, stream);
    } catch (std::exception &ex) {
        WarnL << ex.what();
        return nullptr;
    }
#else
    WarnL << "创建MP4点播失败,请编译时打开\"ENABLE_MP4\"选项";
    return nullptr;
#endif //ENABLE_MP4
}

xiongziliang committed
442 443
/////////////////////////////////////MediaSourceEvent//////////////////////////////////////

444 445 446 447 448 449
void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
    if (size || totalReaderCount(sender)) {
        //还有人观看该视频,不触发关闭事件
        return;
    }
    //没有任何人观看该视频源,表明该源可以关闭了
xiongziliang committed
450
    GET_CONFIG(string, record_app, Record::kAppName);
451
    GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
452
    //如果mp4点播, 无人观看时我们强制关闭点播
xiongziliang committed
453
    bool is_mp4_vod = sender.getApp() == record_app;
454
    weak_ptr<MediaSource> weak_sender = sender.shared_from_this();
455

456 457 458
    _async_close_timer = std::make_shared<Timer>(stream_none_reader_delay / 1000.0, [weak_sender, is_mp4_vod]() {
        auto strong_sender = weak_sender.lock();
        if (!strong_sender) {
459 460 461 462
            //对象已经销毁
            return false;
        }

463 464
        if (strong_sender->totalReaderCount()) {
            //还有人观看该视频,不触发关闭事件
465
            return false;
466
        }
467

468
        if (!is_mp4_vod) {
469 470
            //直播时触发无人观看事件,让开发者自行选择是否关闭
            WarnL << "无人观看事件:"
471 472 473 474 475 476
                  << strong_sender->getSchema() << "/"
                  << strong_sender->getVhost() << "/"
                  << strong_sender->getApp() << "/"
                  << strong_sender->getId();
            NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strong_sender);
        } else {
477 478
            //这个是mp4点播,我们自动关闭
            WarnL << "MP4点播无人观看,自动关闭:"
479 480 481 482 483
                  << strong_sender->getSchema() << "/"
                  << strong_sender->getVhost() << "/"
                  << strong_sender->getApp() << "/"
                  << strong_sender->getId();
            strong_sender->close(false);
484
        }
485 486
        return false;
    }, nullptr);
xiongziliang committed
487 488
}

xiongziliang committed
489 490 491 492
bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) {
    auto listener = _listener.lock();
    if (!listener) {
        return false;
493
    }
xiongziliang committed
494 495 496 497 498 499 500
    return listener->seekTo(sender, stamp);
}

bool MediaSourceEventInterceptor::close(MediaSource &sender, bool force) {
    auto listener = _listener.lock();
    if (!listener) {
        return false;
501
    }
xiongziliang committed
502 503 504 505 506 507 508 509 510 511 512
    return listener->close(sender, force);
}

int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) {
    auto listener = _listener.lock();
    if (!listener) {
        return sender.readerCount();
    }
    return listener->totalReaderCount(sender);
}

513
void MediaSourceEventInterceptor::onReaderChanged(MediaSource &sender, int size) {
xiongziliang committed
514 515
    auto listener = _listener.lock();
    if (!listener) {
516 517 518
        MediaSourceEvent::onReaderChanged(sender, size);
    } else {
        listener->onReaderChanged(sender, size);
xiongziliang committed
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
    }
}

void MediaSourceEventInterceptor::onRegist(MediaSource &sender, bool regist) {
    auto listener = _listener.lock();
    if (listener) {
        listener->onRegist(sender, regist);
    }
}

bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) {
    auto listener = _listener.lock();
    if (!listener) {
        return false;
    }
    return listener->setupRecord(sender, type, start, custom_path);
}

bool MediaSourceEventInterceptor::isRecording(MediaSource &sender, Recorder::type type) {
    auto listener = _listener.lock();
    if (!listener) {
        return false;
    }
    return listener->isRecording(sender, type);
543
}
544

545 546 547 548 549 550 551 552
vector<Track::Ptr> MediaSourceEventInterceptor::getTracks(MediaSource &sender, bool trackReady) const {
    auto listener = _listener.lock();
    if (!listener) {
        return vector<Track::Ptr>();
    }
    return listener->getTracks(sender, trackReady);
}

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
void MediaSourceEventInterceptor::startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb){
    auto listener = _listener.lock();
    if (listener) {
        listener->startSendRtp(sender, dst_url, dst_port, ssrc, is_udp, cb);
    } else {
        MediaSourceEvent::startSendRtp(sender, dst_url, dst_port, ssrc, is_udp, cb);
    }
}

bool MediaSourceEventInterceptor::stopSendRtp(MediaSource &sender){
    auto listener = _listener.lock();
    if (listener) {
        return listener->stopSendRtp(sender);
    }
    return false;
}

xiongziliang committed
570 571
/////////////////////////////////////FlushPolicy//////////////////////////////////////

572
static bool isFlushAble_default(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size) {
573 574
    if (new_stamp + 500 < last_stamp) {
        //时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的
xiongziliang committed
575 576 577
        return true;
    }

578 579
    //时间戳发送变化或者缓存超过1024个,sendmsg接口一般最多只能发送1024个数据包
    return last_stamp != new_stamp || cache_size >= 1024;
xiongziliang committed
580 581
}

582
static bool isFlushAble_merge(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size, int merge_ms) {
583 584
    if (new_stamp + 500 < last_stamp) {
        //时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的
xiongziliang committed
585 586 587
        return true;
    }

588
    if (new_stamp > last_stamp + merge_ms) {
xiongziliang committed
589 590 591 592
        //时间戳增量超过合并写阈值
        return true;
    }

593 594 595
    //缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题
    //而且sendmsg接口一般最多只能发送1024个数据包
    return cache_size >= 1024;
xiongziliang committed
596 597
}

598 599 600 601 602
bool FlushPolicy::isFlushAble(bool is_video, bool is_key, uint32_t new_stamp, int cache_size) {
    bool flush_flag = false;
    if (is_key && is_video) {
        //遇到关键帧flush掉前面的数据,确保关键帧为该组数据的第一帧,确保GOP缓存有效
        flush_flag = true;
603
    } else {
604 605 606 607 608 609 610
        GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
        if (mergeWriteMS <= 0) {
            //关闭了合并写或者合并写阈值小于等于0
            flush_flag = isFlushAble_default(is_video, _last_stamp[is_video], new_stamp, cache_size);
        } else {
            flush_flag = isFlushAble_merge(is_video, _last_stamp[is_video], new_stamp, cache_size, mergeWriteMS);
        }
611 612
    }

613 614
    if (flush_flag) {
        _last_stamp[is_video] = new_stamp;
xiongziliang committed
615
    }
616
    return flush_flag;
xiongziliang committed
617 618
}

xiongziliang committed
619
} /* namespace mediakit */