支持http-ts/websocket-ts直播

This commit is contained in:
xiongziliang 2020-09-20 00:21:46 +08:00
parent f84981dc75
commit c76930e3cd
9 changed files with 371 additions and 80 deletions

View File

@ -13,7 +13,7 @@
## 项目特点
- 基于C++11开发避免使用裸指针代码稳定可靠性能优越。
- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/MP4),支持协议互转。
- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/HTTP-TS/Websocket-TS/MP4),支持协议互转。
- 使用多路复用/多线程/异步网络IO模式开发并发性能优越支持海量客户端连接。
- 代码经过长期大量的稳定性、性能测试,已经在线上商用验证已久。
- 支持linux、macos、ios、android、windows全平台。
@ -60,6 +60,10 @@
- 支持HLS播发器支持拉流HLS转rtsp/rtmp/mp4
- 支持H264/H265/AAC/G711/OPUS编码
- TS
- 支持http[s]-ts直播
- 支持ws[s]-ts直播
- HTTP[S]与WebSocket
- 服务器支持`目录索引生成`,`文件下载`,`表单提交请求`
- 客户端提供`文件下载器(支持断点续传)`,`接口请求器`,`文件上传器`

View File

@ -11,7 +11,7 @@
## Why ZLMediaKit?
- Developed based on C++ 11, the code is stable and reliable, avoiding the use of raw pointers, cross-platform porting is simple and convenient, and the code is clear and concise.
- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/Websocket-flv`),and support Inter-protocol conversion.
- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/WebSocket-flv/HTTP-TS/WebSocket-TS`),and support Inter-protocol conversion.
- Multiplexing asynchronous network IO based on epoll and multi threadextreme performance.
- Well performance and stable test,can be used commercially.
- Support linux, macos, ios, android, Windows Platforms.
@ -31,7 +31,7 @@
- RTMP[S]
- RTMP[S] server,support player and pusher.
- RTMP[S] player and pusher.
- Support HTTP-FLV player.
- Support HTTP-FLV/WebSocket-FLV sever.
- H265/H264/AAC/G711/OPUS codec.
- Recorded as flv or mp4.
- Vod of mp4.
@ -42,6 +42,9 @@
- Play authentication based on cookie.
- Support HLS player, support streaming HLS proxy to RTSP / RTMP / MP4.
- TS
- Support HTTP-TS/WebSocket-TS sever.
- HTTP[S]
- HTTP server,suppor directory meun、RESTful http api.
- HTTP client,downloader,uploader,and http api requester.

View File

@ -32,6 +32,8 @@ MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost, const string &app, con
if (enable_mp4) {
_mp4 = Recorder::createRecorder(Recorder::type_mp4, vhost, app, stream);
}
_ts = std::make_shared<TSMediaSourceMuxer>(vhost, app, stream);
}
void MultiMuxerPrivate::resetTracks() {
@ -41,6 +43,9 @@ void MultiMuxerPrivate::resetTracks() {
if (_rtsp) {
_rtsp->resetTracks();
}
if (_ts) {
_ts->resetTracks();
}
//拷贝智能指针目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto hls = _hls;
@ -62,6 +67,9 @@ void MultiMuxerPrivate::setMediaListener(const std::weak_ptr<MediaSourceEvent> &
if (_rtsp) {
_rtsp->setListener(listener);
}
if (_ts) {
_ts->setListener(listener);
}
auto hls = _hls;
if (hls) {
hls->setListener(listener);
@ -70,7 +78,10 @@ void MultiMuxerPrivate::setMediaListener(const std::weak_ptr<MediaSourceEvent> &
int MultiMuxerPrivate::totalReaderCount() const {
auto hls = _hls;
return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls ? hls->readerCount() : 0);
return (_rtsp ? _rtsp->readerCount() : 0) +
(_rtmp ? _rtmp->readerCount() : 0) +
(_ts ? _ts->readerCount() : 0) +
(hls ? hls->readerCount() : 0) ;
}
static std::shared_ptr<MediaSinkInterface> makeRecorder(const vector<Track::Ptr> &tracks, Recorder::type type, const string &custom_path, MediaSource &sender){
@ -145,6 +156,9 @@ void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) {
if (_rtsp) {
_rtsp->addTrack(track);
}
if (_ts) {
_ts->addTrack(track);
}
//拷贝智能指针目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto hls = _hls;
@ -161,6 +175,7 @@ bool MultiMuxerPrivate::isEnabled(){
auto hls = _hls;
return (_rtmp ? _rtmp->isEnabled() : false) ||
(_rtsp ? _rtsp->isEnabled() : false) ||
(_ts ? _ts->isEnabled() : false) ||
(hls ? hls->isEnabled() : false) || _mp4;
}
@ -171,6 +186,10 @@ void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) {
if (_rtsp) {
_rtsp->inputFrame(frame);
}
if (_ts) {
_ts->inputFrame(frame);
}
//拷贝智能指针目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优
auto hls = _hls;

View File

@ -18,6 +18,7 @@
#include "Record/HlsMediaSource.h"
#include "Rtsp/RtspMediaSourceMuxer.h"
#include "Rtmp/RtmpMediaSourceMuxer.h"
#include "TS/TSMediaSourceMuxer.h"
namespace mediakit{
@ -56,6 +57,7 @@ private:
RtspMediaSourceMuxer::Ptr _rtsp;
HlsRecorder::Ptr _hls;
MediaSinkInterface::Ptr _mp4;
TSMediaSourceMuxer::Ptr _ts;
std::weak_ptr<MediaSourceEvent> _listener;
};

View File

@ -47,6 +47,7 @@ bool loadIniConfig(const char *ini_path = nullptr);
#define RTSP_SCHEMA "rtsp"
#define RTMP_SCHEMA "rtmp"
#define HLS_SCHEMA "hls"
#define TS_SCHEMA "ts"
#define DEFAULT_VHOST "__defaultVhost__"
////////////广播名称///////////

View File

@ -8,15 +8,9 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#if !defined(_WIN32)
#include <dirent.h>
#endif //!defined(_WIN32)
#include <stdio.h>
#include <sys/stat.h>
#include <algorithm>
#include <iomanip>
#include "Common/config.h"
#include "strCoding.h"
#include "HttpSession.h"
@ -96,10 +90,10 @@ void HttpSession::onRecv(const Buffer::Ptr &pBuf) {
}
void HttpSession::onError(const SockException& err) {
if(_is_flv_stream){
if(_is_live_stream){
uint64_t duration = _ticker.createdTime()/1000;
//flv播放器
WarnP(this) << "FLV播放器("
//flv/ts播放器
WarnP(this) << "FLV/TS播放器("
<< _mediaInfo._vhost << "/"
<< _mediaInfo._app << "/"
<< _mediaInfo._streamid
@ -107,8 +101,8 @@ void HttpSession::onError(const SockException& err) {
<< ",耗时(s):" << duration;
GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
if(_ui64TotalBytes > iFlowThreshold * 1024){
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, duration , true, static_cast<SockInfo &>(*this));
if(_total_bytes_usage > iFlowThreshold * 1024){
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _total_bytes_usage, duration , true, static_cast<SockInfo &>(*this));
}
return;
}
@ -135,8 +129,7 @@ bool HttpSession::checkWebSocket(){
if (Sec_WebSocket_Key.empty()) {
return false;
}
auto Sec_WebSocket_Accept = encodeBase64(
SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
KeyValue headerOut;
headerOut["Upgrade"] = "websocket";
@ -147,17 +140,23 @@ bool HttpSession::checkWebSocket(){
}
auto res_cb = [this, headerOut]() {
_flv_over_websocket = true;
_live_over_websocket = true;
sendResponse("101 Switching Protocols", false, nullptr, headerOut, nullptr, true);
};
//判断是否为websocket-flv
if (checkLiveFlvStream(res_cb)) {
if (checkLiveStreamFlv(res_cb)) {
//这里是websocket-flv直播请求
return true;
}
//如果checkLiveFlvStream返回false,则代表不是websocket-flv而是普通的websocket连接
//判断是否为websocket-ts
if (checkLiveStreamTS(res_cb)) {
//这里是websocket-ts直播请求
return true;
}
//这是普通的websocket连接
if (!onWebSocketConnect(_parser)) {
sendResponse("501 Not Implemented", true, nullptr, headerOut);
return true;
@ -166,75 +165,63 @@ bool HttpSession::checkWebSocket(){
return true;
}
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix, const function<void(const MediaSource::Ptr &src)> &cb){
auto pos = strrchr(_parser.Url().data(), '.');
if (!pos) {
//未找到".flv"后缀
//未找到后缀
return false;
}
if(strcasecmp(pos,".flv") != 0){
//未找到".flv"后缀
if (strcasecmp(pos, url_suffix.data()) != 0) {
//未找到直播流后缀
return false;
}
//这是个.flv的流
_mediaInfo.parse(string(RTMP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl());
if(_mediaInfo._app.empty() || _mediaInfo._streamid.size() < 5){
//这是个符合后缀的直播的流
_mediaInfo.parse(schema + "://" + _parser["Host"] + _parser.FullUrl());
if (_mediaInfo._app.empty() || _mediaInfo._streamid.size() < url_suffix.size() + 1) {
//url不合法
return false;
}
_mediaInfo._streamid.erase(_mediaInfo._streamid.size() - 4);//去除.flv后缀
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
//去除后缀
bool close_flag = !strcasecmp(_parser["Connection"].data(), "close");
//流id去除后缀
_mediaInfo._streamid.erase(_mediaInfo._streamid.size() - url_suffix.size());
weak_ptr<HttpSession> weak_self = dynamic_pointer_cast<HttpSession>(shared_from_this());
//鉴权结果回调
auto onRes = [cb, weakSelf, bClose](const string &err){
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
auto onRes = [cb, weak_self, close_flag](const string &err) {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
if (!err.empty()) {
//播放鉴权失败
strongSelf->sendResponse("401 Unauthorized", bClose, nullptr, KeyValue(), std::make_shared<HttpStringBody>(err));
strong_self->sendResponse("401 Unauthorized", close_flag, nullptr, KeyValue(), std::make_shared<HttpStringBody>(err));
return;
}
//异步查找rtmp
MediaSource::findAsync(strongSelf->_mediaInfo, strongSelf, [weakSelf, bClose, cb](const MediaSource::Ptr &src) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
//异步查找直播
MediaSource::findAsync(strong_self->_mediaInfo, strong_self, [weak_self, close_flag, cb](const MediaSource::Ptr &src) {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src);
if (!rtmp_src) {
if (!src) {
//未找到该流
strongSelf->sendNotFound(bClose);
strong_self->sendNotFound(close_flag);
return;
}
if (!cb) {
//找到rtmp源发送http头负载后续发送
strongSelf->sendResponse("200 OK", false, "video/x-flv", KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
}
//http-flv直播牺牲延时提升发送性能
strongSelf->setSocketFlags();
strongSelf->start(strongSelf->getPoller(), rtmp_src);
strongSelf->_is_flv_stream = true;
strong_self->_is_live_stream = true;
//触发回调
cb(src);
});
};
Broadcast::AuthInvoker invoker = [weakSelf, onRes](const string &err) {
auto strongSelf = weakSelf.lock();
Broadcast::AuthInvoker invoker = [weak_self, onRes](const string &err) {
auto strongSelf = weak_self.lock();
if (!strongSelf) {
return;
}
@ -251,6 +238,59 @@ bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
return true;
}
//http-ts 链接格式:http://vhost-url:port/app/streamid.ts?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.ts,那么表明该url是一个http-ts直播。
bool HttpSession::checkLiveStreamTS(const function<void()> &cb){
return checkLiveStream(TS_SCHEMA, ".ts", [this, cb](const MediaSource::Ptr &src) {
auto ts_src = dynamic_pointer_cast<TSMediaSource>(src);
assert(ts_src);
if (!cb) {
//找到源发送http头负载后续发送
sendResponse("200 OK", false, "video/mp2t", KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
}
//直播牺牲延时提升发送性能
setSocketFlags();
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
_ts_reader = ts_src->getRing()->attach(getPoller());
_ts_reader->setReadCB([weakSelf](const TSMediaSource::RingDataType &ts_list) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
//本对象已经销毁
return;
}
int i = 0;
int size = ts_list->size();
strongSelf->setSendFlushFlag(false);
ts_list->for_each([&](const TSPacket::Ptr &ts) {
strongSelf->onWrite(ts, ++i == size);
});
});
});
}
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
bool HttpSession::checkLiveStreamFlv(const function<void()> &cb){
return checkLiveStream(RTMP_SCHEMA, ".flv", [this, cb](const MediaSource::Ptr &src) {
auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src);
assert(rtmp_src);
if (!cb) {
//找到源发送http头负载后续发送
sendResponse("200 OK", false, "video/x-flv", KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
}
//直播牺牲延时提升发送性能
setSocketFlags();
start(getPoller(), rtmp_src);
});
}
void HttpSession::Handle_Req_GET(int64_t &content_len) {
Handle_Req_GET_l(content_len, true);
}
@ -272,13 +312,17 @@ void HttpSession::Handle_Req_GET_l(int64_t &content_len, bool sendBody) {
return;
}
if(checkLiveFlvStream()){
if (checkLiveStreamFlv()) {
//拦截http-flv播放器
return;
}
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
if (checkLiveStreamTS()) {
//拦截http-ts播放器
return;
}
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
HttpFileManager::onAccessPath(*this, _parser, [weakSelf, bClose](const string &status_code, const string &content_type,
const StrCaseMap &responseHeader, const HttpBody::Ptr &body) {
@ -623,8 +667,8 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer, bool flush) {
}
_ticker.resetTime();
if(!_flv_over_websocket){
_ui64TotalBytes += buffer->size();
if (!_live_over_websocket) {
_total_bytes_usage += buffer->size();
send(buffer);
} else {
WebSocketHeader header;
@ -642,7 +686,7 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer, bool flush) {
}
void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){
_ui64TotalBytes += buffer->size();
_total_bytes_usage += buffer->size();
send(buffer);
}

View File

@ -19,6 +19,7 @@
#include "WebSocketSplitter.h"
#include "HttpCookieManager.h"
#include "HttpFileManager.h"
#include "TS/TSMediaSource.h"
using namespace std;
using namespace toolkit;
@ -104,7 +105,11 @@ private:
void Handle_Req_POST(int64_t &content_len);
void Handle_Req_HEAD(int64_t &content_len);
bool checkLiveFlvStream(const function<void()> &cb = nullptr);
bool checkLiveStream(const string &schema, const string &url_suffix, const function<void(const MediaSource::Ptr &src)> &cb);
bool checkLiveStreamFlv(const function<void()> &cb = nullptr);
bool checkLiveStreamTS(const function<void()> &cb = nullptr);
bool checkWebSocket();
bool emitHttpEvent(bool doInvoke);
void urlDecode(Parser &parser);
@ -117,17 +122,17 @@ private:
void setSocketFlags();
private:
bool _is_live_stream = false;
bool _live_over_websocket = false;
//消耗的总流量
uint64_t _total_bytes_usage = 0;
string _origin;
Parser _parser;
Ticker _ticker;
//消耗的总流量
uint64_t _ui64TotalBytes = 0;
//flv over http
MediaInfo _mediaInfo;
TSMediaSource::RingType::RingReader::Ptr _ts_reader;
//处理content数据的callback
function<bool (const char *data,uint64_t len) > _contentCallBack;
bool _flv_over_websocket = false;
bool _is_flv_stream = false;
};

129
src/TS/TSMediaSource.h Normal file
View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* 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.
*/
#ifndef ZLMEDIAKIT_TSMEDIASOURCE_H
#define ZLMEDIAKIT_TSMEDIASOURCE_H
#include "Common/MediaSource.h"
using namespace toolkit;
#define TS_GOP_SIZE 512
namespace mediakit {
//TS直播数据包
class TSPacket : public BufferRaw{
public:
using Ptr = std::shared_ptr<TSPacket>;
template<typename ...ARGS>
TSPacket(ARGS && ...args) : BufferRaw(std::forward<ARGS>(args)...) {};
~TSPacket() override = default;
public:
uint32_t time_stamp = 0;
};
//TS直播合并写策略类
class TSFlushPolicy : public FlushPolicy{
public:
TSFlushPolicy() = default;
~TSFlushPolicy() = default;
uint32_t getStamp(const TSPacket::Ptr &packet) {
return packet->time_stamp;
}
};
//TS直播源
class TSMediaSource : public MediaSource, public RingDelegate<TSPacket::Ptr>, public PacketCache<TSPacket, TSFlushPolicy>{
public:
using PoolType = ResourcePool<TSPacket>;
using Ptr = std::shared_ptr<TSMediaSource>;
using RingDataType = std::shared_ptr<List<TSPacket::Ptr> >;
using RingType = RingBuffer<RingDataType>;
TSMediaSource(const string &vhost,
const string &app,
const string &stream_id,
int ring_size = TS_GOP_SIZE) : MediaSource(TS_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
~TSMediaSource() override = default;
/**
*
*/
const RingType::Ptr &getRing() const {
return _ring;
}
/**
*
*/
int readerCount() override {
return _ring ? _ring->readerCount() : 0;
}
/**
* TS包
* @param packet TS包
* @param key
*/
void onWrite(const TSPacket::Ptr &packet, bool key) override {
if (!_ring) {
createRing();
}
if (key) {
_have_video = true;
}
PacketCache<TSPacket, TSFlushPolicy>::inputPacket(true, packet, key);
}
/**
* GOP缓存
*/
void clearCache() override {
PacketCache<TSPacket, TSFlushPolicy>::clearCache();
_ring->clearCache();
}
private:
void createRing(){
weak_ptr<TSMediaSource> weak_self = dynamic_pointer_cast<TSMediaSource>(shared_from_this());
_ring = std::make_shared<RingType>(_ring_size, [weak_self](int size) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->onReaderChanged(size);
});
onReaderChanged(0);
//注册媒体源
regist();
}
/**
*
* @param packet_list
* @param key_pos
*/
void onFlush(std::shared_ptr<List<TSPacket::Ptr> > &packet_list, bool key_pos) override {
//如果不存在视频那么就没有存在GOP缓存的意义所以确保一直清空GOP缓存
_ring->write(packet_list, _have_video ? key_pos : true);
}
private:
bool _have_video = false;
int _ring_size;
RingType::Ptr _ring;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_TSMEDIASOURCE_H

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* 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.
*/
#ifndef ZLMEDIAKIT_TSMEDIASOURCEMUXER_H
#define ZLMEDIAKIT_TSMEDIASOURCEMUXER_H
#include "TSMediaSource.h"
#include "Record/TsMuxer.h"
namespace mediakit {
class TSMediaSourceMuxer : public TsMuxer, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<TSMediaSourceMuxer> {
public:
using Ptr = std::shared_ptr<TSMediaSourceMuxer>;
TSMediaSourceMuxer(const string &vhost,
const string &app,
const string &stream_id) {
_media_src = std::make_shared<TSMediaSource>(vhost, app, stream_id);
_pool.setSize(256);
}
~TSMediaSourceMuxer() override = default;
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
_listener = listener;
_media_src->setListener(shared_from_this());
}
int readerCount() const{
return _media_src->readerCount();
}
void onReaderChanged(MediaSource &sender, int size) override {
_enabled = size;
if (!size) {
_clear_cache = true;
}
MediaSourceEventInterceptor::onReaderChanged(sender, size);
}
void inputFrame(const Frame::Ptr &frame) override {
if (_clear_cache) {
_clear_cache = false;
_media_src->clearCache();
}
if (_enabled) {
TsMuxer::inputFrame(frame);
}
}
bool isEnabled() {
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return _clear_cache ? true : _enabled;
}
protected:
void onTs(const void *data, int len,uint32_t timestamp,bool is_idr_fast_packet) override{
if(!data || !len){
return;
}
TSPacket::Ptr packet = _pool.obtain();
packet->assign((char *) data, len);
packet->time_stamp = timestamp;
_media_src->onWrite(packet, is_idr_fast_packet);
}
private:
bool _enabled = true;
bool _clear_cache = false;
TSMediaSource::PoolType _pool;
TSMediaSource::Ptr _media_src;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_TSMEDIASOURCEMUXER_H