Merge pull request #19 from xia-chu/master

update
This commit is contained in:
baiyfcu
2020-09-22 17:20:22 +08:00
committed by GitHub
163 changed files with 6399 additions and 4256 deletions

View File

@@ -12,23 +12,17 @@
#include "Util/logger.h"
#include "Util/base64.h"
#include "Extension/AAC.h"
#include "Extension/Opus.h"
#include "Extension/G711.h"
#include "Extension/H264.h"
#include "Extension/H265.h"
using namespace toolkit;
namespace mediakit {
DevChannel::DevChannel(const string &vhost,
const string &app,
const string &stream_id,
float duration,
bool enable_rtsp,
bool enable_rtmp,
bool enable_hls,
bool enable_mp4) :
MultiMediaSourceMuxer(vhost, app, stream_id, duration, enable_rtsp, enable_rtmp, enable_hls, enable_mp4) {}
DevChannel::DevChannel(const string &vhost, const string &app, const string &stream_id,
float duration, bool enable_hls, bool enable_mp4) :
MultiMediaSourceMuxer(vhost, app, stream_id, duration, true, true, enable_hls, enable_mp4) {}
DevChannel::~DevChannel() {}
@@ -109,11 +103,12 @@ void DevChannel::inputH265(const char *data, int len, uint32_t dts, uint32_t pts
inputFrame(frame);
}
class AACFrameCacheAble : public AACFrameNoCacheAble{
class FrameAutoDelete : public FrameFromPtr{
public:
template <typename ... ARGS>
AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward<ARGS>(args)...){};
virtual ~AACFrameCacheAble() {
FrameAutoDelete(ARGS && ...args) : FrameFromPtr(std::forward<ARGS>(args)...){}
~FrameAutoDelete() override {
delete [] _ptr;
};
@@ -123,31 +118,32 @@ public:
};
void DevChannel::inputAAC(const char *data_without_adts, int len, uint32_t dts, const char *adts_header){
if(dts == 0){
dts = (uint32_t)_aTicker[1].elapsedTime();
if (dts == 0) {
dts = (uint32_t) _aTicker[1].elapsedTime();
}
if(adts_header){
if(adts_header + 7 == data_without_adts){
if (adts_header) {
if (adts_header + ADTS_HEADER_LEN == data_without_adts) {
//adts头和帧在一起
inputFrame(std::make_shared<AACFrameNoCacheAble>((char *)data_without_adts - 7, len + 7, dts, 0, 7));
}else{
inputFrame(std::make_shared<FrameFromPtr>(_audio->codecId, (char *) data_without_adts - ADTS_HEADER_LEN, len + ADTS_HEADER_LEN, dts, 0, ADTS_HEADER_LEN));
} else {
//adts头和帧不在一起
char *dataWithAdts = new char[len + 7];
memcpy(dataWithAdts, adts_header, 7);
memcpy(dataWithAdts + 7 , data_without_adts , len);
inputFrame(std::make_shared<AACFrameCacheAble>(dataWithAdts, len + 7, dts, 0, 7));
char *data_with_adts = new char[len + ADTS_HEADER_LEN];
memcpy(data_with_adts, adts_header, ADTS_HEADER_LEN);
memcpy(data_with_adts + ADTS_HEADER_LEN, data_without_adts, len);
inputFrame(std::make_shared<FrameAutoDelete>(_audio->codecId, data_with_adts, len + ADTS_HEADER_LEN, dts, 0, ADTS_HEADER_LEN));
}
} else {
//没有adts头
inputFrame(std::make_shared<FrameFromPtr>(_audio->codecId, (char *) data_without_adts, len, dts, 0, 0));
}
}
void DevChannel::inputG711(const char *data, int len, uint32_t dts){
void DevChannel::inputAudio(const char *data, int len, uint32_t dts){
if (dts == 0) {
dts = (uint32_t)_aTicker[1].elapsedTime();
dts = (uint32_t) _aTicker[1].elapsedTime();
}
auto frame = std::make_shared<G711FrameNoCacheAble>((char*)data, len, dts, 0);
frame->setCodec(_audio->codecId);
inputFrame(frame);
inputFrame(std::make_shared<FrameFromPtr>(_audio->codecId, (char *) data, len, dts, 0));
}
void DevChannel::initVideo(const VideoInfo &info) {
@@ -165,6 +161,7 @@ void DevChannel::initAudio(const AudioInfo &info) {
case CodecAAC : addTrack(std::make_shared<AACTrack>()); break;
case CodecG711A :
case CodecG711U : addTrack(std::make_shared<G711Track>(info.codecId, info.iSampleRate, info.iChannel, info.iSampleBit)); break;
case CodecOpus : addTrack(std::make_shared<OpusTrack>()); break;
default: WarnL << "不支持该类型的音频编码类型:" << info.codecId; break;
}
}

View File

@@ -17,11 +17,9 @@
#include "Util/util.h"
#include "Util/TimeTicker.h"
#include "Common/MultiMediaSourceMuxer.h"
using namespace std;
using namespace toolkit;
#ifdef ENABLE_FAAC
#include "Codec/AACEncoder.h"
#endif //ENABLE_FAAC
@@ -55,16 +53,10 @@ class DevChannel : public MultiMediaSourceMuxer{
public:
typedef std::shared_ptr<DevChannel> Ptr;
//fDuration<=0为直播否则为点播
DevChannel(const string &vhost,
const string &app,
const string &stream_id,
float duration = 0,
bool enable_rtsp = true,
bool enable_rtmp = true,
bool enable_hls = true,
bool enable_mp4 = false);
DevChannel(const string &vhost, const string &app, const string &stream_id,
float duration = 0, bool enable_hls = true, bool enable_mp4 = false);
virtual ~DevChannel();
~DevChannel() override ;
/**
* 初始化视频Track
@@ -108,12 +100,12 @@ public:
void inputAAC(const char *data_without_adts, int len, uint32_t dts, const char *adts_header);
/**
* G711音频帧
* 输入OPUS/G711音频帧
* @param data 音频帧
* @param len 帧数据长度
* @param dts 时间戳,单位毫秒
*/
void inputG711(const char* data, int len, uint32_t dts);
void inputAudio(const char *data, int len, uint32_t dts);
#ifdef ENABLE_X264
/**

View File

@@ -161,7 +161,7 @@ vector<Track::Ptr> MediaSink::getTracks(bool trackReady) const{
}
ret.emplace_back(pr.second);
}
return std::move(ret);
return ret;
}

View File

@@ -36,6 +36,11 @@ public:
*/
virtual void addTrack(const Track::Ptr & track) = 0;
/**
* 添加所有Track完毕
*/
virtual void addTrackCompleted() {}
/**
* 重置track
*/
@@ -70,7 +75,7 @@ public:
* 这样会增加生成流的延时如果添加了音视频双Track那么可以不调用此方法
* 否则为了降低流注册延时,请手动调用此方法
*/
void addTrackCompleted();
void addTrackCompleted() override;
/**
* 重置track

View File

@@ -8,7 +8,6 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <math.h>
#include "MediaSource.h"
#include "Record/MP4Reader.h"
#include "Util/util.h"
@@ -17,16 +16,19 @@
using namespace toolkit;
namespace mediakit {
recursive_mutex MediaSource::g_mtxMediaSrc;
MediaSource::SchemaVhostAppStreamMap MediaSource::g_mapMediaSrc;
recursive_mutex s_media_source_mtx;
MediaSource::SchemaVhostAppStreamMap s_media_source_map;
MediaSource::MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) :
_strSchema(strSchema), _strApp(strApp), _strId(strId) {
if (strVhost.empty()) {
_strVhost = DEFAULT_VHOST;
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;
} else {
_strVhost = strVhost;
_vhost = vhost.empty() ? DEFAULT_VHOST : vhost;
}
_schema = schema;
_app = app;
_stream_id = stream_id;
}
MediaSource::~MediaSource() {
@@ -34,32 +36,28 @@ MediaSource::~MediaSource() {
}
const string& MediaSource::getSchema() const {
return _strSchema;
return _schema;
}
const string& MediaSource::getVhost() const {
return _strVhost;
return _vhost;
}
const string& MediaSource::getApp() const {
//获取该源的id
return _strApp;
return _app;
}
const string& MediaSource::getId() const {
return _strId;
return _stream_id;
}
vector<Track::Ptr> MediaSource::getTracks(bool trackReady) const {
auto strongPtr = _track_source.lock();
if(strongPtr){
return strongPtr->getTracks(trackReady);
vector<Track::Ptr> MediaSource::getTracks(bool ready) const {
auto listener = _listener.lock();
if(!listener){
return vector<Track::Ptr>();
}
return vector<Track::Ptr>();
}
void MediaSource::setTrackSource(const std::weak_ptr<TrackSource> &track_src) {
_track_source = track_src;
return listener->getTracks(const_cast<MediaSource &>(*this), ready);
}
void MediaSource::setListener(const std::weak_ptr<MediaSourceEvent> &listener){
@@ -77,12 +75,13 @@ int MediaSource::totalReaderCount(){
}
return listener->totalReaderCount(*this);
}
bool MediaSource::seekTo(uint32_t ui32Stamp) {
bool MediaSource::seekTo(uint32_t stamp) {
auto listener = _listener.lock();
if(!listener){
return false;
}
return listener->seekTo(*this,ui32Stamp);
return listener->seekTo(*this, stamp);
}
bool MediaSource::close(bool force) {
@@ -93,19 +92,17 @@ bool MediaSource::close(bool force) {
return listener->close(*this,force);
}
void MediaSource::onNoneReader(){
void MediaSource::onReaderChanged(int size) {
auto listener = _listener.lock();
if(!listener){
return;
}
if (listener->totalReaderCount(*this) == 0) {
listener->onNoneReader(*this);
if (listener) {
listener->onReaderChanged(*this, size);
}
}
bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path){
auto listener = _listener.lock();
if (!listener) {
WarnL << "未设置MediaSource的事件监听者setupRecord失败:" << getSchema() << "/" << getVhost() << "/" << getApp() << "/" << getId();
return false;
}
return listener->setupRecord(*this, type, start, custom_path);
@@ -119,13 +116,30 @@ bool MediaSource::isRecording(Recorder::type type){
return listener->isRecording(*this, type);
}
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);
}
void MediaSource::for_each_media(const function<void(const MediaSource::Ptr &src)> &cb) {
decltype(g_mapMediaSrc) copy;
decltype(s_media_source_map) copy;
{
//拷贝g_mapMediaSrc后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码
//拷贝s_media_source_map后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码
//很容易导致多个锁交叉死锁的情况,而且该函数使用频率不高,拷贝开销相对来说是可以接受的
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
copy = g_mapMediaSrc;
lock_guard<recursive_mutex> lock(s_media_source_mtx);
copy = s_media_source_map;
}
for (auto &pr0 : copy) {
@@ -180,42 +194,76 @@ static void eraseIfEmpty(MAP &map, IT0 it0, IT1 it1, IT2 it2) {
}
}
void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptr<TcpSession> &session, bool retry, const function<void(const MediaSource::Ptr &src)> &cb){
auto src = MediaSource::find_l(info._schema, info._vhost, info._app, info._streamid, true);
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 && schema != HLS_SCHEMA){
//未查找媒体源则读取mp4创建一个
//播放hls不触发mp4点播(因为HLS也可以用于录像不是纯粹的直播)
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);
if (src || !retry) {
cb(src);
return;
}
void *listener_tag = session.get();
weak_ptr<TcpSession> weakSession = session;
weak_ptr<TcpSession> weak_session = session;
GET_CONFIG(int, maxWaitMS, General::kMaxStreamWaitTimeMS);
auto onTimeout = session->getPoller()->doDelayTask(maxWaitMS, [cb, listener_tag]() {
auto on_timeout = session->getPoller()->doDelayTask(maxWaitMS, [cb, listener_tag]() {
//最多等待一定时间,如果这个时间内,流未注册上,那么返回未找到流
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
cb(nullptr);
return 0;
});
auto cancelAll = [onTimeout, listener_tag]() {
auto cancel_all = [on_timeout, listener_tag]() {
//取消延时任务,防止多次回调
onTimeout->cancel();
on_timeout->cancel();
//取消媒体注册事件监听
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
};
function<void()> closePlayer = [cb, cancelAll]() {
cancelAll();
function<void()> close_player = [cb, cancel_all]() {
cancel_all();
//告诉播放器,流不存在,这样会立即断开播放器
cb(nullptr);
};
auto onRegist = [weakSession, info, cb, cancelAll](BroadcastMediaChangedArgs) {
auto strongSession = weakSession.lock();
if (!strongSession) {
auto on_regist = [weak_session, info, cb, cancel_all](BroadcastMediaChangedArgs) {
auto strong_session = weak_session.lock();
if (!strong_session) {
//自己已经销毁
cancelAll();
cancel_all();
return;
}
@@ -228,11 +276,11 @@ void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptr<TcpSe
return;
}
cancelAll();
cancel_all();
//播发器请求的流终于注册上了,切换到自己的线程再回复
strongSession->async([weakSession, info, cb]() {
auto strongSession = weakSession.lock();
strong_session->async([weak_session, info, cb]() {
auto strongSession = weak_session.lock();
if (!strongSession) {
return;
}
@@ -243,9 +291,9 @@ void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptr<TcpSe
};
//监听媒体注册事件
NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, onRegist);
NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, on_regist);
//广播未找到流,此时可以立即去拉流,这样还来得及
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastNotFoundStream, info, static_cast<SockInfo &>(*session), closePlayer);
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastNotFoundStream, info, static_cast<SockInfo &>(*session), close_player);
}
void MediaSource::findAsync(const MediaInfo &info, const std::shared_ptr<TcpSession> &session,const function<void(const Ptr &src)> &cb){
@@ -256,232 +304,129 @@ MediaSource::Ptr MediaSource::find(const string &schema, const string &vhost, co
return find_l(schema, vhost, app, id, false);
}
MediaSource::Ptr MediaSource::find_l(const string &schema, const string &vhost_tmp, const string &app, const string &id, bool bMake) {
string vhost = vhost_tmp;
if(vhost.empty()){
vhost = DEFAULT_VHOST;
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;
}
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
if(!enableVhost){
vhost = DEFAULT_VHOST;
src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id);
if (src) {
return src;
}
MediaSource::Ptr ret;
{
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
//查找某一媒体源,找到后返回
searchMedia(g_mapMediaSrc, schema, vhost, app, id, [&](SchemaVhostAppStreamMap::iterator &it0,
VhostAppStreamMap::iterator &it1,
AppStreamMap::iterator &it2,
StreamMap::iterator &it3) {
ret = it3->second.lock();
if (!ret) {
//该对象已经销毁
it2->second.erase(it3);
eraseIfEmpty(g_mapMediaSrc, it0, it1, it2);
return false;
}
return true;
});
}
if(!ret && bMake){
//未查找媒体源,则创建一个
ret = createFromMP4(schema, vhost, app, id);
}
return ret;
return MediaSource::find(HLS_SCHEMA, vhost, app, stream_id);
}
void MediaSource::regist() {
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
if(!enableVhost){
_strVhost = DEFAULT_VHOST;
}
//注册该源,注册后服务器才能找到该源
{
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
g_mapMediaSrc[_strSchema][_strVhost][_strApp][_strId] = shared_from_this();
}
_StrPrinter codec_info;
auto tracks = getTracks(true);
for(auto &track : tracks) {
auto codec_type = track->getTrackType();
codec_info << track->getCodecName();
switch (codec_type) {
case TrackAudio : {
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
codec_info << "["
<< audio_track->getAudioSampleRate() << "/"
<< audio_track->getAudioChannel() << "/"
<< audio_track->getAudioSampleBit() << "] ";
break;
}
case TrackVideo : {
auto video_track = dynamic_pointer_cast<VideoTrack>(track);
codec_info << "["
<< video_track->getVideoWidth() << "/"
<< video_track->getVideoHeight() << "/"
<< round(video_track->getVideoFps()) << "] ";
break;
}
default:
break;
}
}
InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId << " " << codec_info;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, true, *this);
void MediaSource::emitEvent(bool regist){
auto listener = _listener.lock();
if (listener) {
listener->onRegist(*this, true);
//触发回调
listener->onRegist(*this, regist);
}
//触发广播
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, regist, *this);
InfoL << (regist ? "媒体注册:" : "媒体注销:") << _schema << " " << _vhost << " " << _app << " " << _stream_id;
}
void MediaSource::regist() {
{
//减小互斥锁临界区
lock_guard<recursive_mutex> lock(s_media_source_mtx);
s_media_source_map[_schema][_vhost][_app][_stream_id] = shared_from_this();
}
emitEvent(true);
}
//反注册该源
bool MediaSource::unregist() {
bool ret;
{
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
ret = searchMedia(g_mapMediaSrc, _strSchema, _strVhost, _strApp, _strId,
[&](SchemaVhostAppStreamMap::iterator &it0,
VhostAppStreamMap::iterator &it1,
AppStreamMap::iterator &it2,
StreamMap::iterator &it3) {
auto strongMedia = it3->second.lock();
if (strongMedia && this != strongMedia.get()) {
//不是自己,不允许反注册
return false;
}
it2->second.erase(it3);
eraseIfEmpty(g_mapMediaSrc, it0, it1, it2);
return true;
});
//减小互斥锁临界区
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){
InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, false, *this);
auto listener = _listener.lock();
if (listener) {
listener->onRegist(*this, false);
}
if (ret) {
emitEvent(false);
}
return ret;
}
/////////////////////////////////////MediaInfo//////////////////////////////////////
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";
void MediaInfo::parse(const string &url_in){
string url = url_in;
auto pos = url.find("?");
if (pos != string::npos) {
_param_strs = url.substr(pos + 1);
url.erase(pos);
}
auto schema_pos = url.find("://");
if(schema_pos != string::npos){
_schema = url.substr(0,schema_pos);
}else{
if (schema_pos != string::npos) {
_schema = url.substr(0, schema_pos);
} else {
schema_pos = -3;
}
auto split_vec = split(url.substr(schema_pos + 3),"/");
if(split_vec.size() > 0){
auto split_vec = split(url.substr(schema_pos + 3), "/");
if (split_vec.size() > 0) {
auto vhost = split_vec[0];
auto pos = vhost.find(":");
if(pos != string::npos){
_host = _vhost = vhost.substr(0,pos);
if (pos != string::npos) {
_host = _vhost = vhost.substr(0, pos);
_port = vhost.substr(pos + 1);
} else{
} else {
_host = _vhost = vhost;
}
if(_vhost == "localhost" || INADDR_NONE != inet_addr(_vhost.data())){
if (_vhost == "localhost" || INADDR_NONE != inet_addr(_vhost.data())) {
//如果访问的是localhost或ip那么则为默认虚拟主机
_vhost = DEFAULT_VHOST;
}
}
if(split_vec.size() > 1){
if (split_vec.size() > 1) {
_app = split_vec[1];
}
if(split_vec.size() > 2){
string steamid;
for(int i = 2 ; i < split_vec.size() ; ++i){
steamid.append(split_vec[i] + "/");
if (split_vec.size() > 2) {
string stream_id;
for (int i = 2; i < split_vec.size(); ++i) {
stream_id.append(split_vec[i] + "/");
}
if(steamid.back() == '/'){
steamid.pop_back();
}
auto pos = steamid.find("?");
if(pos != string::npos){
_streamid = steamid.substr(0,pos);
_param_strs = steamid.substr(pos + 1);
auto params = Parser::parseArgs(_param_strs);
if(params.find(VHOST_KEY) != params.end()){
_vhost = params[VHOST_KEY];
}
} else{
_streamid = steamid;
if (stream_id.back() == '/') {
stream_id.pop_back();
}
_streamid = stream_id;
}
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
if(!enableVhost || _vhost.empty()){
auto params = Parser::parseArgs(_param_strs);
if (params.find(VHOST_KEY) != params.end()) {
_vhost = params[VHOST_KEY];
}
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
if (!enableVhost || _vhost.empty()) {
//如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认
_vhost = DEFAULT_VHOST;
}
}
/////////////////////////////////////MediaSourceEvent//////////////////////////////////////
void MediaSourceEvent::onNoneReader(MediaSource &sender){
GET_CONFIG(string, recordApp, Record::kAppName);
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
//如果mp4点播, 无人观看时我们强制关闭点播
bool is_mp4_vod = sender.getApp() == recordApp;
//没有任何人观看该视频源,表明该源可以关闭了
weak_ptr<MediaSource> weakSender = sender.shared_from_this();
_async_close_timer = std::make_shared<Timer>(stream_none_reader_delay / 1000.0, [weakSender,is_mp4_vod]() {
auto strongSender = weakSender.lock();
if (!strongSender) {
//对象已经销毁
return false;
}
if (strongSender->totalReaderCount() != 0) {
//还有人消费
return false;
}
if(!is_mp4_vod){
//直播时触发无人观看事件,让开发者自行选择是否关闭
WarnL << "无人观看事件:"
<< strongSender->getSchema() << "/"
<< strongSender->getVhost() << "/"
<< strongSender->getApp() << "/"
<< strongSender->getId();
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strongSender);
}else{
//这个是mp4点播我们自动关闭
WarnL << "MP4点播无人观看,自动关闭:"
<< strongSender->getSchema() << "/"
<< strongSender->getVhost() << "/"
<< strongSender->getApp() << "/"
<< strongSender->getId();
strongSender->close(false);
}
return false;
}, nullptr);
}
MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &filePath , bool checkApp){
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 (checkApp && app != appName) {
if (check_app && app != appName) {
return nullptr;
}
#ifdef ENABLE_MP4
try {
MP4Reader::Ptr pReader(new MP4Reader(vhost, app, stream, filePath));
MP4Reader::Ptr pReader(new MP4Reader(vhost, app, stream, file_path));
pReader->startReadMP4();
return MediaSource::find(schema, vhost, app, stream);
} catch (std::exception &ex) {
@@ -494,6 +439,136 @@ MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string &
#endif //ENABLE_MP4
}
/////////////////////////////////////MediaSourceEvent//////////////////////////////////////
void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
if (size || totalReaderCount(sender)) {
//还有人观看该视频,不触发关闭事件
return;
}
//没有任何人观看该视频源,表明该源可以关闭了
GET_CONFIG(string, record_app, Record::kAppName);
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
//如果mp4点播, 无人观看时我们强制关闭点播
bool is_mp4_vod = sender.getApp() == record_app;
weak_ptr<MediaSource> weak_sender = sender.shared_from_this();
_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) {
//对象已经销毁
return false;
}
if (strong_sender->totalReaderCount()) {
//还有人观看该视频,不触发关闭事件
return false;
}
if (!is_mp4_vod) {
//直播时触发无人观看事件,让开发者自行选择是否关闭
WarnL << "无人观看事件:"
<< strong_sender->getSchema() << "/"
<< strong_sender->getVhost() << "/"
<< strong_sender->getApp() << "/"
<< strong_sender->getId();
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strong_sender);
} else {
//这个是mp4点播我们自动关闭
WarnL << "MP4点播无人观看,自动关闭:"
<< strong_sender->getSchema() << "/"
<< strong_sender->getVhost() << "/"
<< strong_sender->getApp() << "/"
<< strong_sender->getId();
strong_sender->close(false);
}
return false;
}, nullptr);
}
bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) {
auto listener = _listener.lock();
if (!listener) {
return false;
}
return listener->seekTo(sender, stamp);
}
bool MediaSourceEventInterceptor::close(MediaSource &sender, bool force) {
auto listener = _listener.lock();
if (!listener) {
return false;
}
return listener->close(sender, force);
}
int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) {
auto listener = _listener.lock();
if (!listener) {
return sender.readerCount();
}
return listener->totalReaderCount(sender);
}
void MediaSourceEventInterceptor::onReaderChanged(MediaSource &sender, int size) {
auto listener = _listener.lock();
if (!listener) {
MediaSourceEvent::onReaderChanged(sender, size);
} else {
listener->onReaderChanged(sender, size);
}
}
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);
}
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);
}
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;
}
/////////////////////////////////////FlushPolicy//////////////////////////////////////
static bool isFlushAble_default(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size) {
if (new_stamp + 500 < last_stamp) {
//时间戳回退比较大(可能seek中)由于rtp中时间戳是pts是可能存在一定程度的回退的

View File

@@ -44,33 +44,63 @@ public:
virtual ~MediaSourceEvent(){};
// 通知拖动进度条
virtual bool seekTo(MediaSource &sender,uint32_t ui32Stamp){ return false; }
// 通知其停止
virtual bool close(MediaSource &sender,bool force) { return false;}
// 观看总人数
virtual bool seekTo(MediaSource &sender, uint32_t stamp) { return false; }
// 通知其停止产生
virtual bool close(MediaSource &sender, bool force) { return false; }
// 获取观看总人数
virtual int totalReaderCount(MediaSource &sender) = 0;
// 通知观看人数变化
virtual void onReaderChanged(MediaSource &sender, int size);
//流注册或注销事件
virtual void onRegist(MediaSource &sender, bool regist) {};
////////////////////////仅供MultiMediaSourceMuxer对象继承////////////////////////
// 开启或关闭录制
virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { return false; };
// 获取录制状态
virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; };
// 通知无人观看
virtual void onNoneReader(MediaSource &sender);
//流注册或注销事件
virtual void onRegist(MediaSource &sender, bool regist) {};
// 获取所有track相关信息
virtual vector<Track::Ptr> getTracks(MediaSource &sender, bool trackReady = true) const { return vector<Track::Ptr>(); };
// 开始发送ps-rtp
virtual void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb) { cb(SockException(Err_other, "not implemented"));};
// 停止发送ps-rtp
virtual bool stopSendRtp(MediaSource &sender) {return false; }
private:
Timer::Ptr _async_close_timer;
};
//该对象用于拦截感兴趣的MediaSourceEvent事件
class MediaSourceEventInterceptor : public MediaSourceEvent{
public:
MediaSourceEventInterceptor(){}
~MediaSourceEventInterceptor() override {}
bool seekTo(MediaSource &sender, uint32_t stamp) override;
bool close(MediaSource &sender, bool force) override;
int totalReaderCount(MediaSource &sender) override;
void onReaderChanged(MediaSource &sender, int size) override;
void onRegist(MediaSource &sender, bool regist) override;
bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) override;
bool isRecording(MediaSource &sender, Recorder::type type) override;
vector<Track::Ptr> getTracks(MediaSource &sender, bool trackReady = true) const override;
void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb) override;
bool stopSendRtp(MediaSource &sender) override;
protected:
std::weak_ptr<MediaSourceEvent> _listener;
};
/**
* 解析url获取媒体相关信息
*/
class MediaInfo{
public:
MediaInfo(){}
~MediaInfo(){}
MediaInfo(const string &url){ parse(url); }
~MediaInfo() {}
MediaInfo() {}
MediaInfo(const string &url) { parse(url); }
void parse(const string &url);
public:
string _schema;
string _host;
@@ -92,9 +122,11 @@ public:
typedef unordered_map<string, AppStreamMap > VhostAppStreamMap;
typedef unordered_map<string, VhostAppStreamMap > SchemaVhostAppStreamMap;
MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) ;
MediaSource(const string &schema, const string &vhost, const string &app, const string &stream_id) ;
virtual ~MediaSource() ;
////////////////获取MediaSource相关信息////////////////
// 获取协议类型
const string& getSchema() const;
// 虚拟主机
@@ -104,13 +136,18 @@ public:
// 流id
const string& getId() const;
// 设置TrackSource
void setTrackSource(const std::weak_ptr<TrackSource> &track_src);
// 获取所有Track
vector<Track::Ptr> getTracks(bool trackReady = true) const override;
vector<Track::Ptr> getTracks(bool ready = true) const override;
// 获取流当前时间戳
virtual uint32_t getTimeStamp(TrackType type) { return 0; };
// 设置时间戳
virtual void setTimeStamp(uint32_t stamp) {};
////////////////MediaSourceEvent相关接口实现////////////////
// 设置监听者
virtual void setListener(const std::weak_ptr<MediaSourceEvent> &listener);
void setListener(const std::weak_ptr<MediaSourceEvent> &listener);
// 获取监听者
const std::weak_ptr<MediaSourceEvent>& getListener() const;
@@ -119,48 +156,52 @@ public:
// 观看者个数,包括(hls/rtsp/rtmp)
virtual int totalReaderCount();
// 获取流当前时间戳
virtual uint32_t getTimeStamp(TrackType trackType) { return 0; };
// 设置时间戳
virtual void setTimeStamp(uint32_t uiStamp) {};
// 拖动进度条
bool seekTo(uint32_t ui32Stamp);
bool seekTo(uint32_t stamp);
// 关闭该流
bool close(bool force);
// 该流无人观看
void onNoneReader();
// 该流观看人数变化
void onReaderChanged(int size);
// 开启或关闭录制
virtual bool setupRecord(Recorder::type type, bool start, const string &custom_path);
bool setupRecord(Recorder::type type, bool start, const string &custom_path);
// 获取录制状态
virtual bool isRecording(Recorder::type type);
bool isRecording(Recorder::type type);
// 开始发送ps-rtp
void startSendRtp(const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb);
// 停止发送ps-rtp
bool stopSendRtp();
////////////////static方法查找或生成MediaSource////////////////
// 同步查找流
static Ptr find(const string &schema, const string &vhost, const string &app, const string &id);
// 忽略类型同步查找流可能返回rtmp/rtsp/hls类型
static Ptr find(const string &vhost, const string &app, const string &stream_id);
// 异步查找流
static void findAsync(const MediaInfo &info, const std::shared_ptr<TcpSession> &session, const function<void(const Ptr &src)> &cb);
// 遍历所有流
static void for_each_media(const function<void(const Ptr &src)> &cb);
// 从mp4文件生成MediaSource
static MediaSource::Ptr createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &filePath = "", bool checkApp = true);
static MediaSource::Ptr createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &file_path = "", bool check_app = true);
protected:
void regist() ;
bool unregist();
//媒体注册
void regist();
private:
static Ptr find_l(const string &schema, const string &vhost, const string &app, const string &id, bool bMake);
static void findAsync_l(const MediaInfo &info, const std::shared_ptr<TcpSession> &session, bool retry, const function<void(const MediaSource::Ptr &src)> &cb);
//媒体注销
bool unregist();
//触发媒体事件
void emitEvent(bool regist);
private:
string _strSchema;
string _strVhost;
string _strApp;
string _strId;
string _schema;
string _vhost;
string _app;
string _stream_id;
std::weak_ptr<MediaSourceEvent> _listener;
weak_ptr<TrackSource> _track_source;
static SchemaVhostAppStreamMap g_mapMediaSrc;
static recursive_mutex g_mtxMediaSrc;
};
///缓存刷新策略类
@@ -174,7 +215,7 @@ public:
}
uint32_t getStamp(const RtmpPacket::Ptr &packet) {
return packet->timeStamp;
return packet->time_stamp;
}
bool isFlushAble(bool is_video, bool is_key, uint32_t new_stamp, int cache_size);
@@ -208,6 +249,10 @@ public:
}
}
virtual void clearCache() {
_cache->clear();
}
virtual void onFlush(std::shared_ptr<packet_list> &, bool key_pos) = 0;
private:
@@ -221,9 +266,9 @@ private:
}
private:
bool _key_pos = false;
policy _policy;
std::shared_ptr<packet_list> _cache;
bool _key_pos = false;
};
} /* namespace mediakit */

View File

@@ -8,36 +8,33 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <math.h>
#include "MultiMediaSourceMuxer.h"
namespace mediakit {
///////////////////////////////MultiMuxerPrivate//////////////////////////////////
MultiMuxerPrivate::~MultiMuxerPrivate() {}
MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost,
const string &app,
const string &stream,
float dur_sec,
bool enable_rtsp,
bool enable_rtmp,
bool enable_hls,
bool enable_mp4) {
MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost, const string &app, const string &stream, float dur_sec,
bool enable_rtsp, bool enable_rtmp, bool enable_hls, bool enable_mp4) {
_stream_url = vhost + " " + app + " " + stream;
if (enable_rtmp) {
_rtmp = std::make_shared<RtmpMediaSourceMuxer>(vhost, app, stream, std::make_shared<TitleMeta>(dur_sec));
_enable_rtxp = true;
}
if (enable_rtsp) {
_rtsp = std::make_shared<RtspMediaSourceMuxer>(vhost, app, stream, std::make_shared<TitleSdp>(dur_sec));
_enable_rtxp = true;
}
if (enable_hls) {
_hls = Recorder::createRecorder(Recorder::type_hls, vhost, app, stream);
_enable_record = true;
_hls = dynamic_pointer_cast<HlsRecorder>(Recorder::createRecorder(Recorder::type_hls, vhost, app, stream));
}
if (enable_mp4) {
_mp4 = Recorder::createRecorder(Recorder::type_mp4, vhost, app, stream);
_enable_record = true;
}
_ts = std::make_shared<TSMediaSourceMuxer>(vhost, app, stream);
_fmp4 = std::make_shared<FMP4MediaSourceMuxer>(vhost, app, stream);
}
void MultiMuxerPrivate::resetTracks() {
@@ -47,6 +44,12 @@ void MultiMuxerPrivate::resetTracks() {
if (_rtsp) {
_rtsp->resetTracks();
}
if (_ts) {
_ts->resetTracks();
}
if (_fmp4) {
_fmp4->resetTracks();
}
//拷贝智能指针目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto hls = _hls;
@@ -61,24 +64,32 @@ void MultiMuxerPrivate::resetTracks() {
}
void MultiMuxerPrivate::setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener) {
_listener = listener;
if (_rtmp) {
_rtmp->setListener(listener);
}
if (_rtsp) {
_rtsp->setListener(listener);
}
auto hls_src = getHlsMediaSource();
if (hls_src) {
hls_src->setListener(listener);
if (_ts) {
_ts->setListener(listener);
}
if (_fmp4) {
_fmp4->setListener(listener);
}
auto hls = _hls;
if (hls) {
hls->setListener(listener);
}
_meida_listener = listener;
}
int MultiMuxerPrivate::totalReaderCount() const {
auto hls_src = getHlsMediaSource();
return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0);
auto hls = _hls;
return (_rtsp ? _rtsp->readerCount() : 0) +
(_rtmp ? _rtmp->readerCount() : 0) +
(_ts ? _ts->readerCount() : 0) +
(_fmp4 ? _fmp4->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){
@@ -95,18 +106,16 @@ bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bo
case Recorder::type_hls : {
if (start && !_hls) {
//开始录制
_hls = makeRecorder(getTracks(true), type, custom_path, sender);
auto hls_src = getHlsMediaSource();
if (hls_src) {
auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(getTracks(true), type, custom_path, sender));
if (hls) {
//设置HlsMediaSource的事件监听器
hls_src->setListener(_meida_listener);
hls_src->setTrackSource(shared_from_this());
hls->setListener(_listener);
}
_hls = hls;
} else if (!start && _hls) {
//停止录制
_hls = nullptr;
}
_enable_record = _hls || _mp4;
return true;
}
case Recorder::type_mp4 : {
@@ -117,7 +126,6 @@ bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bo
//停止录制
_mp4 = nullptr;
}
_enable_record = _hls || _mp4;
return true;
}
default : return false;
@@ -146,7 +154,7 @@ void MultiMuxerPrivate::setTimeStamp(uint32_t stamp) {
}
void MultiMuxerPrivate::setTrackListener(Listener *listener) {
_listener = listener;
_track_listener = listener;
}
void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) {
@@ -156,6 +164,12 @@ void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) {
if (_rtsp) {
_rtsp->addTrack(track);
}
if (_ts) {
_ts->addTrack(track);
}
if (_fmp4) {
_fmp4->addTrack(track);
}
//拷贝智能指针目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto hls = _hls;
@@ -169,7 +183,12 @@ void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) {
}
bool MultiMuxerPrivate::isEnabled(){
return _enable_rtxp || _enable_record;
auto hls = _hls;
return (_rtmp ? _rtmp->isEnabled() : false) ||
(_rtsp ? _rtsp->isEnabled() : false) ||
(_ts ? _ts->isEnabled() : false) ||
(_fmp4 ? _fmp4->isEnabled() : false) ||
(hls ? hls->isEnabled() : false) || _mp4;
}
void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) {
@@ -179,6 +198,13 @@ void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) {
if (_rtsp) {
_rtsp->inputFrame(frame);
}
if (_ts) {
_ts->inputFrame(frame);
}
if (_fmp4) {
_fmp4->inputFrame(frame);
}
//拷贝智能指针目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优
auto hls = _hls;
@@ -191,51 +217,70 @@ void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) {
}
}
static string getTrackInfoStr(const TrackSource *track_src){
_StrPrinter codec_info;
auto tracks = track_src->getTracks(true);
for (auto &track : tracks) {
auto codec_type = track->getTrackType();
codec_info << track->getCodecName();
switch (codec_type) {
case TrackAudio : {
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
codec_info << "["
<< audio_track->getAudioSampleRate() << "/"
<< audio_track->getAudioChannel() << "/"
<< audio_track->getAudioSampleBit() << "] ";
break;
}
case TrackVideo : {
auto video_track = dynamic_pointer_cast<VideoTrack>(track);
codec_info << "["
<< video_track->getVideoWidth() << "/"
<< video_track->getVideoHeight() << "/"
<< round(video_track->getVideoFps()) << "] ";
break;
}
default:
break;
}
}
return codec_info;
}
void MultiMuxerPrivate::onAllTrackReady() {
if (_rtmp) {
_rtmp->setTrackSource(shared_from_this());
_rtmp->onAllTrackReady();
}
if (_rtsp) {
_rtsp->setTrackSource(shared_from_this());
_rtsp->onAllTrackReady();
}
auto hls_src = getHlsMediaSource();
if (hls_src) {
hls_src->setTrackSource(shared_from_this());
if (_fmp4) {
_fmp4->onAllTrackReady();
}
if (_listener) {
_listener->onAllTrackReady();
if (_track_listener) {
_track_listener->onAllTrackReady();
}
InfoL << "stream: " << _stream_url << " , codec info: " << getTrackInfoStr(this);
}
MediaSource::Ptr MultiMuxerPrivate::getHlsMediaSource() const {
auto recorder = dynamic_pointer_cast<HlsRecorder>(_hls);
if (recorder) {
return recorder->getMediaSource();
}
return nullptr;
}
/////////////////////////////////////////////////////////////////
///////////////////////////////MultiMediaSourceMuxer//////////////////////////////////
MultiMediaSourceMuxer::~MultiMediaSourceMuxer() {}
MultiMediaSourceMuxer::MultiMediaSourceMuxer(const string &vhost,
const string &app,
const string &stream,
float dur_sec,
bool enable_rtsp,
bool enable_rtmp,
bool enable_hls,
bool enable_mp4) {
MultiMediaSourceMuxer::MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec,
bool enable_rtsp, bool enable_rtmp, bool enable_hls, bool enable_mp4) {
_muxer.reset(new MultiMuxerPrivate(vhost, app, stream, dur_sec, enable_rtsp, enable_rtmp, enable_hls, enable_mp4));
_muxer->setTrackListener(this);
}
void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener) {
_muxer->setMediaListener(shared_from_this());
_listener = listener;
//拦截事件
_muxer->setMediaListener(shared_from_this());
}
void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr<MultiMuxerPrivate::Listener> &listener) {
_track_listener = listener;
}
int MultiMediaSourceMuxer::totalReaderCount() const {
@@ -246,62 +291,57 @@ void MultiMediaSourceMuxer::setTimeStamp(uint32_t stamp) {
_muxer->setTimeStamp(stamp);
}
void MultiMediaSourceMuxer::setTrackListener(Listener *listener) {
_muxer->setTrackListener(listener);
}
vector<Track::Ptr> MultiMediaSourceMuxer::getTracks(bool trackReady) const {
vector<Track::Ptr> MultiMediaSourceMuxer::getTracks(MediaSource &sender, bool trackReady) const {
return _muxer->getTracks(trackReady);
}
bool MultiMediaSourceMuxer::seekTo(MediaSource &sender, uint32_t ui32Stamp) {
auto listener = _listener.lock();
if (!listener) {
return false;
}
return listener->seekTo(sender, ui32Stamp);
}
bool MultiMediaSourceMuxer::close(MediaSource &sender, bool force) {
auto listener = _listener.lock();
if (!listener) {
return false;
}
return listener->close(sender, force);
}
int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
auto listener = _listener.lock();
if (!listener) {
return _muxer->totalReaderCount();
return totalReaderCount();
}
return listener->totalReaderCount(sender);
}
void MultiMediaSourceMuxer::onNoneReader(MediaSource &sender){
auto listener = _listener.lock();
if (!listener) {
MediaSourceEvent::onNoneReader(sender);
return;
}
listener->onNoneReader(sender);
}
void MultiMediaSourceMuxer::onRegist(MediaSource &sender, bool regist){
auto listener = _listener.lock();
if (listener) {
listener->onRegist(sender, regist);
}
}
bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) {
return _muxer->setupRecord(sender,type,start,custom_path);
return _muxer->setupRecord(sender, type, start, custom_path);
}
bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) {
return _muxer->isRecording(sender,type);
}
void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb){
#if defined(ENABLE_RTPPROXY)
auto ps_rtp_sender = std::make_shared<PSRtpSender>(ssrc);
weak_ptr<MultiMediaSourceMuxer> weak_self = shared_from_this();
ps_rtp_sender->startSend(dst_url, dst_port, is_udp, [weak_self, ps_rtp_sender, cb](const SockException &ex) {
cb(ex);
auto strong_self = weak_self.lock();
if (!strong_self || ex) {
return;
}
for (auto &track : strong_self->_muxer->getTracks(false)) {
ps_rtp_sender->addTrack(track);
}
ps_rtp_sender->addTrackCompleted();
strong_self->_ps_rtp_sender = ps_rtp_sender;
});
#else
cb(SockException(Err_other, "该功能未启用编译时请打开ENABLE_RTPPROXY宏"));
#endif//ENABLE_RTPPROXY
}
bool MultiMediaSourceMuxer::stopSendRtp(MediaSource &sender){
#if defined(ENABLE_RTPPROXY)
if (_ps_rtp_sender) {
_ps_rtp_sender = nullptr;
return true;
}
#endif//ENABLE_RTPPROXY
return false;
}
void MultiMediaSourceMuxer::addTrack(const Track::Ptr &track) {
_muxer->addTrack(track);
}
@@ -310,6 +350,14 @@ void MultiMediaSourceMuxer::addTrackCompleted() {
_muxer->addTrackCompleted();
}
void MultiMediaSourceMuxer::onAllTrackReady(){
_muxer->setMediaListener(shared_from_this());
auto listener = _track_listener.lock();
if(listener){
listener->onAllTrackReady();
}
}
void MultiMediaSourceMuxer::resetTracks() {
_muxer->resetTracks();
}
@@ -361,27 +409,36 @@ public:
return _frame->getCodecId();
}
private:
Frame::Ptr _frame;
int64_t _dts;
int64_t _pts;
Frame::Ptr _frame;
};
void MultiMediaSourceMuxer::inputFrame(const Frame::Ptr &frame) {
GET_CONFIG(bool,modify_stamp,General::kModifyStamp);
if(!modify_stamp){
//未开启时间戳覆盖
_muxer->inputFrame(frame);
}else{
void MultiMediaSourceMuxer::inputFrame(const Frame::Ptr &frame_in) {
GET_CONFIG(bool, modify_stamp, General::kModifyStamp);
auto frame = frame_in;
if (modify_stamp) {
//开启了时间戳覆盖
FrameModifyStamp::Ptr new_frame = std::make_shared<FrameModifyStamp>(frame,_stamp[frame->getTrackType()]);
//输入时间戳覆盖后的帧
_muxer->inputFrame(new_frame);
frame = std::make_shared<FrameModifyStamp>(frame, _stamp[frame->getTrackType()]);
}
_muxer->inputFrame(frame);
#if defined(ENABLE_RTPPROXY)
auto ps_rtp_sender = _ps_rtp_sender;
if (ps_rtp_sender) {
ps_rtp_sender->inputFrame(frame);
}
#endif //ENABLE_RTPPROXY
}
bool MultiMediaSourceMuxer::isEnabled(){
#if defined(ENABLE_RTPPROXY)
return (_muxer->isEnabled() || _ps_rtp_sender);
#else
return _muxer->isEnabled();
#endif //ENABLE_RTPPROXY
}
}//namespace mediakit
}//namespace mediakit

View File

@@ -10,14 +10,20 @@
#ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#include "Common/Stamp.h"
#include "Rtp/PSRtpSender.h"
#include "Record/Recorder.h"
#include "Record/HlsRecorder.h"
#include "Record/HlsMediaSource.h"
#include "Rtsp/RtspMediaSourceMuxer.h"
#include "Rtmp/RtmpMediaSourceMuxer.h"
#include "Record/Recorder.h"
#include "Record/HlsMediaSource.h"
#include "Record/HlsRecorder.h"
#include "TS/TSMediaSourceMuxer.h"
#include "FMP4/FMP4MediaSourceMuxer.h"
namespace mediakit{
class MultiMuxerPrivate : public MediaSink , public std::enable_shared_from_this<MultiMuxerPrivate>{
class MultiMuxerPrivate : public MediaSink, public std::enable_shared_from_this<MultiMuxerPrivate>{
public:
friend class MultiMediaSourceMuxer;
typedef std::shared_ptr<MultiMuxerPrivate> Ptr;
@@ -27,17 +33,12 @@ public:
virtual ~Listener() = default;
virtual void onAllTrackReady() = 0;
};
~MultiMuxerPrivate() override ;
private:
MultiMuxerPrivate(const string &vhost,
const string &app,
const string &stream,
float dur_sec,
bool enable_rtsp,
bool enable_rtmp,
bool enable_hls,
bool enable_mp4);
~MultiMuxerPrivate() override;
private:
MultiMuxerPrivate(const string &vhost,const string &app, const string &stream,float dur_sec,
bool enable_rtsp, bool enable_rtmp, bool enable_hls, bool enable_mp4);
void resetTracks() override;
void setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener);
int totalReaderCount() const;
@@ -46,83 +47,67 @@ private:
bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path);
bool isRecording(MediaSource &sender, Recorder::type type);
bool isEnabled();
private:
void onTrackReady(const Track::Ptr & track) override;
void onTrackFrame(const Frame::Ptr &frame) override;
void onAllTrackReady() override;
MediaSource::Ptr getHlsMediaSource() const;
private:
string _stream_url;
Listener *_track_listener = nullptr;
RtmpMediaSourceMuxer::Ptr _rtmp;
RtspMediaSourceMuxer::Ptr _rtsp;
MediaSinkInterface::Ptr _hls;
HlsRecorder::Ptr _hls;
MediaSinkInterface::Ptr _mp4;
Listener *_listener = nullptr;
std::weak_ptr<MediaSourceEvent> _meida_listener;
bool _enable_rtxp = false;
bool _enable_record = false;
TSMediaSourceMuxer::Ptr _ts;
FMP4MediaSourceMuxer::Ptr _fmp4;
std::weak_ptr<MediaSourceEvent> _listener;
};
class MultiMediaSourceMuxer : public MediaSourceEvent, public MediaSinkInterface, public TrackSource, public std::enable_shared_from_this<MultiMediaSourceMuxer>{
class MultiMediaSourceMuxer : public MediaSourceEventInterceptor, public MediaSinkInterface, public MultiMuxerPrivate::Listener, public std::enable_shared_from_this<MultiMediaSourceMuxer>{
public:
typedef MultiMuxerPrivate::Listener Listener;
typedef std::shared_ptr<MultiMediaSourceMuxer> Ptr;
~MultiMediaSourceMuxer() override;
MultiMediaSourceMuxer(const string &vhost,
const string &app,
const string &stream,
float dur_sec = 0.0,
bool enable_rtsp = true,
bool enable_rtmp = true,
bool enable_hls = true,
bool enable_mp4 = false);
MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec = 0.0,
bool enable_rtsp = true, bool enable_rtmp = true, bool enable_hls = true, bool enable_mp4 = false);
/**
* 设置事件监听器
* @param listener
* @param listener 监听器
*/
void setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener);
/**
* 随着Track就绪事件监听器
* @param listener 事件监听器
*/
void setTrackListener(const std::weak_ptr<MultiMuxerPrivate::Listener> &listener);
/**
* 返回总的消费者个数
* @return
*/
int totalReaderCount() const;
/**
* 判断是否生效(是否正在转其他协议)
*/
bool isEnabled();
/**
* 设置MediaSource时间戳
* @param stamp 时间戳
*/
void setTimeStamp(uint32_t stamp);
/**
* 随着Track就绪事件监听器
* @param listener 事件监听器
*/
void setTrackListener(Listener *listener);
/////////////////////////////////MediaSourceEvent override/////////////////////////////////
/**
* 获取所有Track
* @param trackReady 是否筛选过滤未就绪的track
* @return 所有Track
*/
vector<Track::Ptr> getTracks(bool trackReady = true) const override;
/**
* 通知拖动进度条
* @param sender 事件发送者
* @param ui32Stamp 目标时间戳
* @return 是否成功
*/
bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override;
/**
* 通知停止流生成
* @param sender 事件发送者
* @param force 是否强制关闭
* @return 成功与否
*/
bool close(MediaSource &sender,bool force) override;
vector<Track::Ptr> getTracks(MediaSource &sender, bool trackReady = true) const override;
/**
* 观看总人数
@@ -131,19 +116,6 @@ public:
*/
int totalReaderCount(MediaSource &sender) override;
/**
* 触发无人观看事件
* @param sender 触发者
*/
void onNoneReader(MediaSource &sender) override;
/**
* 媒体注册注销事件
* @param sender 触发者
* @param regist 是否为注册事件
*/
void onRegist(MediaSource &sender, bool regist) override;
/**
* 设置录制状态
* @param type 录制类型
@@ -160,18 +132,35 @@ public:
*/
bool isRecording(MediaSource &sender, Recorder::type type) override;
/**
* 开始发送ps-rtp流
* @param dst_url 目标ip或域名
* @param dst_port 目标端口
* @param ssrc rtp的ssrc
* @param is_udp 是否为udp
* @param cb 启动成功或失败回调
*/
void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function<void(const SockException &ex)> &cb) override;
/**
* 停止ps-rtp发送
* @return 是否成功
*/
bool stopSendRtp(MediaSource &sender) override;
/////////////////////////////////MediaSinkInterface override/////////////////////////////////
/**
* 添加track内部会调用Track的clone方法
* 只会克隆sps pps这些信息 而不会克隆Delegate相关关系
* @param track
* @param track 添加音频或视频轨道
*/
void addTrack(const Track::Ptr & track) override;
void addTrack(const Track::Ptr &track) override;
/**
* 添加track完毕
* @param track
*/
void addTrackCompleted();
void addTrackCompleted() override;
/**
* 重置track
@@ -184,14 +173,20 @@ public:
*/
void inputFrame(const Frame::Ptr &frame) override;
/////////////////////////////////MultiMuxerPrivate::Listener override/////////////////////////////////
/**
* 判断是否生效(是否正在转其他协议)
* 所有track全部就绪
*/
bool isEnabled();
void onAllTrackReady() override;
private:
MultiMuxerPrivate::Ptr _muxer;
std::weak_ptr<MediaSourceEvent> _listener;
Stamp _stamp[2];
MultiMuxerPrivate::Ptr _muxer;
std::weak_ptr<MultiMuxerPrivate::Listener> _track_listener;
#if defined(ENABLE_RTPPROXY)
PSRtpSender::Ptr _ps_rtp_sender;
#endif //ENABLE_RTPPROXY
};
}//namespace mediakit

View File

@@ -44,77 +44,79 @@ void Stamp::setPlayBack(bool playback) {
void Stamp::syncTo(Stamp &other){
_sync_master = &other;
_sync_finished = false;
}
//限制dts回退
void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) {
revise_l(dts,pts,dts_out,pts_out,modifyStamp);
if(_sync_finished || modifyStamp || _playback){
//自动生成时间戳或回放或同步完毕
if(dts_out < 0) { dts_out = 0; }
if(pts_out < 0) { pts_out = 0; }
revise_l(dts, pts, dts_out, pts_out, modifyStamp);
if (_playback) {
//回放允许时间戳回退
return;
}
if(_sync_master && _sync_master->_last_dts){
if (dts_out < _last_dts_out) {
WarnL << "dts回退:" << dts_out << " < " << _last_dts_out;
dts_out = _last_dts_out;
pts_out = _last_pts_out;
return;
}
_last_dts_out = dts_out;
_last_pts_out = pts_out;
}
//音视频时间戳同步
void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) {
revise_l2(dts, pts, dts_out, pts_out, modifyStamp);
if (!_sync_master || modifyStamp || _playback) {
//自动生成时间戳或回放或同步完毕
return;
}
if (_sync_master && _sync_master->_last_dts_in) {
//音视频dts当前时间差
int64_t dts_diff = _last_dts - _sync_master->_last_dts;
if(ABS(dts_diff) < 5000){
int64_t dts_diff = _last_dts_in - _sync_master->_last_dts_in;
if (ABS(dts_diff) < 5000) {
//如果绝对时间戳小于5秒那么说明他们的起始时间戳是一致的那么强制同步
_last_relativeStamp = _relativeStamp;
_relativeStamp = _sync_master->_relativeStamp + dts_diff;
_relative_stamp = _sync_master->_relative_stamp + dts_diff;
}
//下次不用再强制同步
_sync_master = nullptr;
}
if (dts_out < 0 || dts_out < _last_relativeStamp) {
//相对时间戳小于0或者小于上次的时间戳
//那么说明是同步时间戳导致的,在这个过渡期内,我们一直返回上次的结果(目的是为了防止时间戳回退)
pts_out = _last_relativeStamp + (pts_out - dts_out);
dts_out = _last_relativeStamp;
} else if(!_sync_master){
//音视频同步过渡期完毕
_sync_finished = true;
}
if(pts_out < 0){
pts_out = dts_out;
}
}
void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) {
if(!pts){
//求取相对时间戳
void Stamp::revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) {
if (!pts) {
//没有播放时间戳,使其赋值为解码时间戳
pts = dts;
}
if(_playback){
if (_playback) {
//这是点播
dts_out = dts;
pts_out = pts;
_relativeStamp = dts_out;
_last_dts = dts;
_relative_stamp = dts_out;
_last_dts_in = dts;
return;
}
//pts和dts的差值
int pts_dts_diff = pts - dts;
if(_last_dts != dts){
if (_last_dts_in != dts) {
//时间戳发生变更
if(modifyStamp){
if (modifyStamp) {
//内部自己生产时间戳
_relativeStamp = _ticker.elapsedTime();
}else{
_relativeStamp += deltaStamp(dts);
_relative_stamp = _ticker.elapsedTime();
} else {
_relative_stamp += deltaStamp(dts);
}
_last_dts = dts;
_last_dts_in = dts;
}
dts_out = _relativeStamp;
dts_out = _relative_stamp;
//////////////以下是播放时间戳的计算//////////////////
if(ABS(pts_dts_diff) > MAX_CTS){
if (ABS(pts_dts_diff) > MAX_CTS) {
//如果差值太大,则认为由于回环导致时间戳错乱了
pts_dts_diff = 0;
}
@@ -123,11 +125,11 @@ void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_ou
}
void Stamp::setRelativeStamp(int64_t relativeStamp) {
_relativeStamp = relativeStamp;
_relative_stamp = relativeStamp;
}
int64_t Stamp::getRelativeStamp() const {
return _relativeStamp;
return _relative_stamp;
}
bool DtsGenerator::getDts(uint32_t pts, uint32_t &dts){

View File

@@ -29,6 +29,7 @@ public:
* @return 时间戳增量
*/
int64_t deltaStamp(int64_t stamp);
private:
int64_t _last_stamp = 0;
};
@@ -41,7 +42,7 @@ public:
~Stamp() = default;
/**
* 修正时间戳
* 求取相对时间戳,同时实现了音视频同步、限制dts回退等功能
* @param dts 输入dts如果为0则根据系统时间戳生成
* @param pts 输入pts如果为0则等于dts
* @param dts_out 输出dts
@@ -75,15 +76,20 @@ public:
void syncTo(Stamp &other);
private:
//主要实现音视频时间戳同步功能
void revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false);
//主要实现获取相对时间戳功能
void revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false);
private:
int64_t _relativeStamp = 0;
int64_t _last_relativeStamp = 0;
int64_t _last_dts = 0;
int64_t _relative_stamp = 0;
int64_t _last_dts_in = 0;
int64_t _last_dts_out = 0;
int64_t _last_pts_out = 0;
SmoothTicker _ticker;
bool _playback = false;
Stamp *_sync_master = nullptr;
bool _sync_finished = true;
};
//dts生成器
@@ -93,8 +99,10 @@ public:
DtsGenerator() = default;
~DtsGenerator() = default;
bool getDts(uint32_t pts, uint32_t &dts);
private:
bool getDts_l(uint32_t pts, uint32_t &dts);
private:
uint32_t _dts_pts_offset = 0;
uint32_t _last_dts = 0;

View File

@@ -40,6 +40,7 @@ bool loadIniConfig(const char *ini_path){
namespace Broadcast {
const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
const string kBroadcastRecordTs = "kBroadcastRecoredTs";
const string kBroadcastHttpRequest = "kBroadcastHttpRequest";
const string kBroadcastHttpAccess = "kBroadcastHttpAccess";
const string kBroadcastOnGetRtspRealm = "kBroadcastOnGetRtspRealm";
@@ -63,7 +64,6 @@ const string kMaxStreamWaitTimeMS = GENERAL_FIELD"maxStreamWaitMS";
const string kEnableVhost = GENERAL_FIELD"enableVhost";
const string kAddMuteAudio = GENERAL_FIELD"addMuteAudio";
const string kResetWhenRePlay = GENERAL_FIELD"resetWhenRePlay";
const string kPublishToRtxp = GENERAL_FIELD"publishToRtxp";
const string kPublishToHls = GENERAL_FIELD"publishToHls";
const string kPublishToMP4 = GENERAL_FIELD"publishToMP4";
const string kMergeWriteMS = GENERAL_FIELD"mergeWriteMS";
@@ -76,7 +76,6 @@ onceToken token([](){
mINI::Instance()[kEnableVhost] = 0;
mINI::Instance()[kAddMuteAudio] = 1;
mINI::Instance()[kResetWhenRePlay] = 1;
mINI::Instance()[kPublishToRtxp] = 1;
mINI::Instance()[kPublishToHls] = 1;
mINI::Instance()[kPublishToMP4] = 0;
mINI::Instance()[kMergeWriteMS] = 0;
@@ -253,6 +252,8 @@ const string kSegmentRetain = HLS_FIELD"segRetain";
const string kFileBufSize = HLS_FIELD"fileBufSize";
//录制文件路径
const string kFilePath = HLS_FIELD"filePath";
// 是否广播 ts 切片完成通知
const string kBroadcastRecordTs = HLS_FIELD"broadcastRecordTs";
onceToken token([](){
mINI::Instance()[kSegmentDuration] = 2;
@@ -260,6 +261,7 @@ onceToken token([](){
mINI::Instance()[kSegmentRetain] = 5;
mINI::Instance()[kFileBufSize] = 64 * 1024;
mINI::Instance()[kFilePath] = "./www";
mINI::Instance()[kBroadcastRecordTs] = false;
},nullptr);
} //namespace Hls

View File

@@ -47,6 +47,8 @@ bool loadIniConfig(const char *ini_path = nullptr);
#define RTSP_SCHEMA "rtsp"
#define RTMP_SCHEMA "rtmp"
#define HLS_SCHEMA "hls"
#define TS_SCHEMA "ts"
#define FMP4_SCHEMA "fmp4"
#define DEFAULT_VHOST "__defaultVhost__"
////////////广播名称///////////
@@ -58,7 +60,11 @@ extern const string kBroadcastMediaChanged;
//录制mp4文件成功后广播
extern const string kBroadcastRecordMP4;
#define BroadcastRecordMP4Args const MP4Info &info
#define BroadcastRecordMP4Args const RecordInfo &info
// 录制 ts 文件后广播
extern const string kBroadcastRecordTs;
#define BroadcastRecordTsArgs const RecordInfo &info
//收到http api请求广播
extern const string kBroadcastHttpRequest;
@@ -86,8 +92,7 @@ extern const string kBroadcastOnRtspAuth;
//如果errMessage为空则代表鉴权成功
//enableHls: 是否允许转换hls
//enableMP4: 是否运行MP4录制
//enableRtxp: rtmp推流时是否运行转rtsprtsp推流时是否允许转rtmp
typedef std::function<void(const string &errMessage,bool enableRtxp,bool enableHls,bool enableMP4)> PublishAuthInvoker;
typedef std::function<void(const string &errMessage, bool enableHls, bool enableMP4)> PublishAuthInvoker;
//收到rtsp/rtmp推流事件广播通过该事件控制推流鉴权
extern const string kBroadcastMediaPublish;
@@ -165,8 +170,6 @@ extern const string kAddMuteAudio;
//拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
//如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
extern const string kResetWhenRePlay;
//是否默认推流时转换成rtsp或rtmphook接口(on_publish)中可以覆盖该设置
extern const string kPublishToRtxp ;
//是否默认推流时转换成hlshook接口(on_publish)中可以覆盖该设置
extern const string kPublishToHls ;
//是否默认推流时mp4录像hook接口(on_publish)中可以覆盖该设置
@@ -284,6 +287,8 @@ extern const string kSegmentRetain;
extern const string kFileBufSize;
//录制文件路径
extern const string kFilePath;
// 是否广播 ts 切片完成通知
extern const string kBroadcastRecordTs;
} //namespace Hls
////////////Rtp代理相关配置///////////

View File

@@ -92,6 +92,16 @@ static void parseAacConfig(const string &config, AdtsHeader &adts) {
adts.no_raw_data_blocks_in_frame = 0;
}
int getAacFrameLength(const uint8_t *data, int bytes) {
uint16_t len;
if (bytes < 7) return -1;
if (0xFF != data[0] || 0xF0 != (data[1] & 0xF0)) {
return -1;
}
len = ((uint16_t) (data[3] & 0x03) << 11) | ((uint16_t) data[4] << 3) | ((uint16_t) (data[5] >> 5) & 0x07);
return len;
}
string makeAacConfig(const uint8_t *hex, int length){
#ifndef ENABLE_MP4
if (!(hex[0] == 0xFF && (hex[1] & 0xF0) == 0xF0)) {
@@ -134,7 +144,7 @@ int dumpAacConfig(const string &config, int length, uint8_t *out, int out_size)
#ifndef ENABLE_MP4
AdtsHeader header;
parseAacConfig(config, header);
header.aac_frame_length = length;
header.aac_frame_length = ADTS_HEADER_LEN + length;
dumpAdtsHeader(header, out);
return ADTS_HEADER_LEN;
#else

View File

@@ -18,44 +18,10 @@
namespace mediakit{
string makeAacConfig(const uint8_t *hex, int length);
int getAacFrameLength(const uint8_t *hex, int length);
int dumpAacConfig(const string &config, int length, uint8_t *out, int out_size);
bool parseAacConfig(const string &config, int &samplerate, int &channels);
/**
* aac帧包含adts头
*/
class AACFrame : public FrameImp {
public:
typedef std::shared_ptr<AACFrame> Ptr;
AACFrame(){
_codecid = CodecAAC;
}
};
class AACFrameNoCacheAble : public FrameFromPtr {
public:
typedef std::shared_ptr<AACFrameNoCacheAble> Ptr;
AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,uint32_t pts = 0,int prefix_size = ADTS_HEADER_LEN){
_ptr = ptr;
_size = size;
_dts = dts;
_prefix_size = prefix_size;
}
CodecId getCodecId() const override{
return CodecAAC;
}
bool keyFrame() const override {
return false;
}
bool configFrame() const override{
return false;
}
};
/**
* aac音频通道
*/
@@ -135,6 +101,28 @@ public:
* @param frame 数据帧
*/
void inputFrame(const Frame::Ptr &frame) override{
if (frame->prefixSize()) {
//有adts头尝试分帧
auto ptr = frame->data();
auto end = frame->data() + frame->size();
while (ptr < end) {
auto frame_len = getAacFrameLength((uint8_t *) ptr, end - ptr);
if (frame_len < ADTS_HEADER_LEN) {
break;
}
auto sub_frame = std::make_shared<FrameInternal<FrameFromPtr> >(frame, (char *) ptr, frame_len, ADTS_HEADER_LEN);
ptr += frame_len;
sub_frame->setCodecId(CodecAAC);
inputFrame_l(sub_frame);
}
} else {
inputFrame_l(frame);
}
}
private:
void inputFrame_l(const Frame::Ptr &frame) {
if (_cfg.empty()) {
//未获取到aac_cfg信息
if (frame->prefixSize()) {
@@ -151,7 +139,6 @@ public:
AudioTrack::inputFrame(frame);
}
}
private:
/**
* 解析2个字节的aac配置
*/

View File

@@ -21,29 +21,30 @@ static string getAacCfg(const RtmpPacket &thiz) {
if (!thiz.isCfgFrame()) {
return ret;
}
if (thiz.strBuf.size() < 4) {
if (thiz.buffer.size() < 4) {
WarnL << "bad aac cfg!";
return ret;
}
ret = thiz.strBuf.substr(2);
ret = thiz.buffer.substr(2);
return ret;
}
bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) {
void AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
if (pkt->isCfgFrame()) {
_aac_cfg = getAacCfg(*pkt);
onGetAAC(nullptr, 0, 0);
return false;
return;
}
if (!_aac_cfg.empty()) {
onGetAAC(pkt->strBuf.data() + 2, pkt->strBuf.size() - 2, pkt->timeStamp);
onGetAAC(pkt->buffer.data() + 2, pkt->buffer.size() - 2, pkt->time_stamp);
}
return false;
}
void AACRtmpDecoder::onGetAAC(const char* data, int len, uint32_t stamp) {
auto frame = ResourcePoolHelper<AACFrame>::obtainObj();
auto frame = ResourcePoolHelper<FrameImp>::obtainObj();
frame->_codec_id = CodecAAC;
//生成adts头
char adts_header[32] = {0};
auto size = dumpAacConfig(_aac_cfg, len, (uint8_t *) adts_header, sizeof(adts_header));
@@ -95,43 +96,43 @@ void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if(!_aac_cfg.empty()){
RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper<RtmpPacket>::obtainObj();
rtmpPkt->strBuf.clear();
rtmpPkt->buffer.clear();
//header
uint8_t is_config = false;
rtmpPkt->strBuf.push_back(_audio_flv_flags);
rtmpPkt->strBuf.push_back(!is_config);
rtmpPkt->buffer.push_back(_audio_flv_flags);
rtmpPkt->buffer.push_back(!is_config);
//aac data
rtmpPkt->strBuf.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
rtmpPkt->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
rtmpPkt->bodySize = rtmpPkt->strBuf.size();
rtmpPkt->chunkId = CHUNK_AUDIO;
rtmpPkt->streamId = STREAM_MEDIA;
rtmpPkt->timeStamp = frame->dts();
rtmpPkt->typeId = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt, false);
rtmpPkt->body_size = rtmpPkt->buffer.size();
rtmpPkt->chunk_id = CHUNK_AUDIO;
rtmpPkt->stream_index = STREAM_MEDIA;
rtmpPkt->time_stamp = frame->dts();
rtmpPkt->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt);
}
}
void AACRtmpEncoder::makeAudioConfigPkt() {
_audio_flv_flags = getAudioRtmpFlags(std::make_shared<AACTrack>(_aac_cfg));
RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper<RtmpPacket>::obtainObj();
rtmpPkt->strBuf.clear();
rtmpPkt->buffer.clear();
//header
uint8_t is_config = true;
rtmpPkt->strBuf.push_back(_audio_flv_flags);
rtmpPkt->strBuf.push_back(!is_config);
rtmpPkt->buffer.push_back(_audio_flv_flags);
rtmpPkt->buffer.push_back(!is_config);
//aac config
rtmpPkt->strBuf.append(_aac_cfg);
rtmpPkt->buffer.append(_aac_cfg);
rtmpPkt->bodySize = rtmpPkt->strBuf.size();
rtmpPkt->chunkId = CHUNK_AUDIO;
rtmpPkt->streamId = STREAM_MEDIA;
rtmpPkt->timeStamp = 0;
rtmpPkt->typeId = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt, false);
rtmpPkt->body_size = rtmpPkt->buffer.size();
rtmpPkt->chunk_id = CHUNK_AUDIO;
rtmpPkt->stream_index = STREAM_MEDIA;
rtmpPkt->time_stamp = 0;
rtmpPkt->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt);
}
}//namespace mediakit

View File

@@ -19,7 +19,7 @@ namespace mediakit{
/**
* aac Rtmp转adts类
*/
class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper<AACFrame> {
class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper<FrameImp> {
public:
typedef std::shared_ptr<AACRtmpDecoder> Ptr;
@@ -28,10 +28,9 @@ public:
/**
* 输入Rtmp并解码
* @param Rtmp Rtmp数据包
* @param key_pos 此参数内部强制转换为false,请忽略之
* @param rtmp Rtmp数据包
*/
bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override;
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
CodecId getCodecId() const override{
return CodecAAC;

View File

@@ -9,7 +9,6 @@
*/
#include "AACRtp.h"
#define AAC_MAX_FRAME_SIZE (2 * 1024)
namespace mediakit{
@@ -68,60 +67,73 @@ AACRtpDecoder::AACRtpDecoder(const Track::Ptr &track) {
} else {
_aac_cfg = aacTrack->getAacCfg();
}
_frame = obtainFrame();
obtainFrame();
}
AACRtpDecoder::AACRtpDecoder() {
_frame = obtainFrame();
obtainFrame();
}
AACFrame::Ptr AACRtpDecoder::obtainFrame() {
void AACRtpDecoder::obtainFrame() {
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
auto frame = ResourcePoolHelper<AACFrame>::obtainObj();
frame->_prefix_size = 0;
frame->_buffer.clear();
return frame;
_frame = ResourcePoolHelper<FrameImp>::obtainObj();
_frame->_prefix_size = 0;
_frame->_buffer.clear();
_frame->_codec_id = CodecAAC;
}
bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) {
//rtp数据开始部分
uint8_t *ptr = (uint8_t *) rtppack->data() + rtppack->offset;
//rtp数据末尾
const uint8_t *end = (uint8_t *) rtppack->data() + rtppack->size();
uint8_t *end = (uint8_t *) rtppack->data() + rtppack->size();
//首2字节表示Au-Header的个数单位bit所以除以16得到Au-Header个数
const uint16_t au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4;
//忽略Au-Header
ptr += 2 + au_header_count * 2;
uint16_t au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4;
//记录au_header起始指针
uint8_t *au_header_ptr = ptr + 2;
ptr = au_header_ptr + au_header_count * 2;
while (ptr < end) {
auto size = (uint32_t) (end - ptr);
if (size > AAC_MAX_FRAME_SIZE) {
size = AAC_MAX_FRAME_SIZE;
if (end < ptr) {
//数据不够
return false;
}
if (!_last_dts) {
//记录第一个时间戳
_last_dts = rtppack->timeStamp;
}
//每个audio unit时间戳增量
auto dts_inc = (rtppack->timeStamp - _last_dts) / au_header_count;
if (dts_inc < 0 && dts_inc > 100) {
//时间戳增量异常,忽略
dts_inc = 0;
}
for (int i = 0; i < au_header_count; ++i) {
// 之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度低3位无用
uint16_t size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3;
if (ptr + size > end) {
//数据不够
break;
}
if (_frame->size() + size > AAC_MAX_FRAME_SIZE) {
//数据太多了,先清空
if (size) {
//设置aac数据
_frame->_buffer.assign((char *) ptr, size);
//设置当前audio unit时间戳
_frame->_dts = _last_dts + i * dts_inc;
ptr += size;
au_header_ptr += 2;
flushData();
}
//追加aac数据
_frame->_buffer.append((char *) ptr, size);
_frame->_dts = rtppack->timeStamp;
ptr += size;
}
if (rtppack->mark) {
//最后一个rtp分片
flushData();
}
//记录上次时间戳
_last_dts = rtppack->timeStamp;
return false;
}
void AACRtpDecoder::flushData() {
if (_frame->_buffer.empty()) {
//没有有效数据
return;
}
//插入adts头
char adts_header[32] = {0};
auto size = dumpAacConfig(_aac_cfg, _frame->_buffer.size(), (uint8_t *) adts_header, sizeof(adts_header));
@@ -131,11 +143,7 @@ void AACRtpDecoder::flushData() {
_frame->_prefix_size = size;
}
RtpCodec::inputFrame(_frame);
_frame = obtainFrame();
obtainFrame();
}
}//namespace mediakit
}//namespace mediakit

View File

@@ -17,7 +17,7 @@ namespace mediakit{
/**
* aac rtp转adts类
*/
class AACRtpDecoder : public RtpCodec , public ResourcePoolHelper<AACFrame> {
class AACRtpDecoder : public RtpCodec , public ResourcePoolHelper<FrameImp> {
public:
typedef std::shared_ptr<AACRtpDecoder> Ptr;
@@ -39,12 +39,13 @@ protected:
AACRtpDecoder();
private:
AACFrame::Ptr obtainFrame();
void obtainFrame();
void flushData();
private:
AACFrame::Ptr _frame;
FrameImp::Ptr _frame;
string _aac_cfg;
uint32_t _last_dts = 0;
};

View File

@@ -0,0 +1,66 @@
/*
* 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.
*/
#include "CommonRtmp.h"
namespace mediakit{
CommonRtmpDecoder::CommonRtmpDecoder(CodecId codec) {
_codec = codec;
obtainFrame();
}
CodecId CommonRtmpDecoder::getCodecId() const {
return _codec;
}
void CommonRtmpDecoder::obtainFrame() {
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
_frame = ResourcePoolHelper<FrameImp>::obtainObj();
_frame->_buffer.clear();
_frame->_codec_id = _codec;
_frame->_prefix_size = 0;
}
void CommonRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp) {
//拷贝负载
_frame->_buffer.assign(rtmp->buffer.data() + 1, rtmp->buffer.size() - 1);
_frame->_dts = rtmp->time_stamp;
//写入环形缓存
RtmpCodec::inputFrame(_frame);
//创建下一帧
obtainFrame();
}
/////////////////////////////////////////////////////////////////////////////////////
CommonRtmpEncoder::CommonRtmpEncoder(const Track::Ptr &track) : CommonRtmpDecoder(track->getCodecId()) {
_audio_flv_flags = getAudioRtmpFlags(track);
}
void CommonRtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if (!_audio_flv_flags) {
return;
}
RtmpPacket::Ptr rtmp = ResourcePoolHelper<RtmpPacket>::obtainObj();
rtmp->buffer.clear();
//header
rtmp->buffer.push_back(_audio_flv_flags);
//data
rtmp->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
rtmp->body_size = rtmp->buffer.size();
rtmp->chunk_id = CHUNK_AUDIO;
rtmp->stream_index = STREAM_MEDIA;
rtmp->time_stamp = frame->dts();
rtmp->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmp);
}
}//namespace mediakit

View File

@@ -0,0 +1,73 @@
/*
* 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_COMMONRTMP_H
#define ZLMEDIAKIT_COMMONRTMP_H
#include "Frame.h"
#include "Rtmp/RtmpCodec.h"
namespace mediakit{
/**
* 通用 rtmp解码类
*/
class CommonRtmpDecoder : public RtmpCodec , public ResourcePoolHelper<FrameImp> {
public:
typedef std::shared_ptr<CommonRtmpDecoder> Ptr;
~CommonRtmpDecoder() override {}
/**
* 构造函数
* @param codec 编码id
*/
CommonRtmpDecoder(CodecId codec);
/**
* 返回编码类型ID
*/
CodecId getCodecId() const override;
/**
* 输入Rtmp并解码
* @param rtmp Rtmp数据包
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
private:
void obtainFrame();
private:
CodecId _codec;
FrameImp::Ptr _frame;
};
/**
* 通用 rtmp编码类
*/
class CommonRtmpEncoder : public CommonRtmpDecoder , public ResourcePoolHelper<RtmpPacket> {
public:
typedef std::shared_ptr<CommonRtmpEncoder> Ptr;
CommonRtmpEncoder(const Track::Ptr &track);
~CommonRtmpEncoder() override{}
/**
* 输入帧数据
*/
void inputFrame(const Frame::Ptr &frame) override;
private:
uint8_t _audio_flv_flags = 0;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_COMMONRTMP_H

View File

@@ -0,0 +1,86 @@
/*
* 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.
*/
#include "CommonRtp.h"
CommonRtpDecoder::CommonRtpDecoder(CodecId codec, int max_frame_size ){
_codec = codec;
_max_frame_size = max_frame_size;
obtainFrame();
}
CodecId CommonRtpDecoder::getCodecId() const {
return _codec;
}
void CommonRtpDecoder::obtainFrame() {
_frame = ResourcePoolHelper<FrameImp>::obtainObj();
_frame->_buffer.clear();
_frame->_prefix_size = 0;
_frame->_dts = 0;
_frame->_codec_id = _codec;
}
bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){
auto payload = rtp->data() + rtp->offset;
auto size = rtp->size() - rtp->offset;
if (size <= 0) {
//无实际负载
return false;
}
if (_frame->_dts != rtp->timeStamp || _frame->_buffer.size() > _max_frame_size) {
//时间戳发生变化或者缓存超过MAX_FRAME_SIZE则清空上帧数据
if (!_frame->_buffer.empty()) {
//有有效帧,则输出
RtpCodec::inputFrame(_frame);
}
//新的一帧数据
obtainFrame();
_frame->_dts = rtp->timeStamp;
_drop_flag = false;
} else if (_last_seq != 0 && (uint16_t)(_last_seq + 1) != rtp->sequence) {
//时间戳未发生变化但是seq却不连续说明中间rtp丢包了那么整帧应该废弃
WarnL << "rtp丢包:" << _last_seq << " -> " << rtp->sequence;
_drop_flag = true;
_frame->_buffer.clear();
}
if (!_drop_flag) {
_frame->_buffer.append(payload, size);
}
_last_seq = rtp->sequence;
return false;
}
////////////////////////////////////////////////////////////////
CommonRtpEncoder::CommonRtpEncoder(CodecId codec, uint32_t ssrc, uint32_t mtu_size,
uint32_t sample_rate, uint8_t payload_type, uint8_t interleaved)
: CommonRtpDecoder(codec), RtpInfo(ssrc, mtu_size, sample_rate, payload_type, interleaved) {
}
void CommonRtpEncoder::inputFrame(const Frame::Ptr &frame){
GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS);
auto stamp = frame->dts() % cycleMS;
auto ptr = frame->data() + frame->prefixSize();
auto len = frame->size() - frame->prefixSize();
auto remain_size = len;
const auto max_rtp_size = _ui32MtuSize - 20;
while (remain_size > 0) {
auto rtp_size = remain_size > max_rtp_size ? max_rtp_size : remain_size;
RtpCodec::inputRtp(makeRtp(getTrackType(), ptr, rtp_size, false, stamp), false);
ptr += rtp_size;
remain_size -= rtp_size;
}
}

85
src/Extension/CommonRtp.h Normal file
View File

@@ -0,0 +1,85 @@
/*
* 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_COMMONRTP_H
#define ZLMEDIAKIT_COMMONRTP_H
#include "Frame.h"
#include "Rtsp/RtpCodec.h"
namespace mediakit{
/**
* 通用 rtp解码类
*/
class CommonRtpDecoder : public RtpCodec, public ResourcePoolHelper<FrameImp> {
public:
typedef std::shared_ptr <CommonRtpDecoder> Ptr;
~CommonRtpDecoder() override {}
/**
* 构造函数
* @param codec 编码id
* @param max_frame_size 允许的最大帧大小
*/
CommonRtpDecoder(CodecId codec, int max_frame_size = 2 * 1024);
/**
* 返回编码类型ID
*/
CodecId getCodecId() const override;
/**
* 输入rtp并解码
* @param rtp rtp数据包
* @param key_pos 此参数内部强制转换为false,请忽略之
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override;
private:
void obtainFrame();
private:
bool _drop_flag = false;
uint16_t _last_seq = 0;
int _max_frame_size;
CodecId _codec;
FrameImp::Ptr _frame;
};
/**
* 通用 rtp编码类
*/
class CommonRtpEncoder : public CommonRtpDecoder, public RtpInfo {
public:
typedef std::shared_ptr <CommonRtpEncoder> Ptr;
~CommonRtpEncoder() override {}
/**
* 构造函数
* @param codec 编码类型
* @param ssrc ssrc
* @param mtu_size mtu 大小
* @param sample_rate 采样率
* @param payload_type pt类型
* @param interleaved rtsp interleaved 值
*/
CommonRtpEncoder(CodecId codec, uint32_t ssrc, uint32_t mtu_size, uint32_t sample_rate, uint8_t payload_type, uint8_t interleaved);
/**
* 输入帧数据并编码成rtp
*/
void inputFrame(const Frame::Ptr &frame) override;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_COMMONRTP_H

View File

@@ -13,11 +13,13 @@
#include "H264Rtmp.h"
#include "H265Rtmp.h"
#include "AACRtmp.h"
#include "G711Rtmp.h"
#include "CommonRtmp.h"
#include "H264Rtp.h"
#include "AACRtp.h"
#include "G711Rtp.h"
#include "H265Rtp.h"
#include "CommonRtp.h"
#include "Opus.h"
#include "G711.h"
#include "Common/Parser.h"
namespace mediakit{
@@ -42,6 +44,10 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
return std::make_shared<AACTrack>(aac_cfg);
}
if (strcasecmp(track->_codec.data(), "opus") == 0) {
return std::make_shared<OpusTrack>();
}
if (strcasecmp(track->_codec.data(), "PCMA") == 0) {
return std::make_shared<G711Track>(CodecG711A, track->_samplerate, track->_channel, 16);
}
@@ -114,11 +120,12 @@ RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) {
auto interleaved = sdp->getTrackType() * 2;
auto codec_id = sdp->getCodecId();
switch (codec_id){
case CodecH264 : return std::make_shared<H264RtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved);
case CodecH265 : return std::make_shared<H265RtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved);
case CodecAAC : return std::make_shared<AACRtpEncoder>(ssrc,mtu,sample_rate,pt,interleaved);
case CodecH264 : return std::make_shared<H264RtpEncoder>(ssrc, mtu, sample_rate, pt, interleaved);
case CodecH265 : return std::make_shared<H265RtpEncoder>(ssrc, mtu, sample_rate, pt, interleaved);
case CodecAAC : return std::make_shared<AACRtpEncoder>(ssrc, mtu, sample_rate, pt, interleaved);
case CodecOpus :
case CodecG711A :
case CodecG711U : return std::make_shared<G711RtpEncoder>(codec_id, ssrc, mtu, sample_rate, pt, interleaved);
case CodecG711U : return std::make_shared<CommonRtpEncoder>(codec_id, ssrc, mtu, sample_rate, pt, interleaved);
default : WarnL << "暂不支持该CodecId:" << codec_id; return nullptr;
}
}
@@ -128,8 +135,9 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) {
case CodecH264 : return std::make_shared<H264RtpDecoder>();
case CodecH265 : return std::make_shared<H265RtpDecoder>();
case CodecAAC : return std::make_shared<AACRtpDecoder>(track->clone());
case CodecOpus :
case CodecG711A :
case CodecG711U : return std::make_shared<G711RtpDecoder>(track->getCodecId());
case CodecG711U : return std::make_shared<CommonRtpDecoder>(track->getCodecId());
default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr;
}
}
@@ -137,40 +145,35 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) {
/////////////////////////////rtmp相关///////////////////////////////////////////
static CodecId getVideoCodecIdByAmf(const AMFValue &val){
if (val.type() == AMF_STRING){
if (val.type() == AMF_STRING) {
auto str = val.as_string();
if(str == "avc1"){
if (str == "avc1") {
return CodecH264;
}
if(str == "mp4a"){
return CodecAAC;
}
if(str == "hev1" || str == "hvc1"){
if (str == "hev1" || str == "hvc1") {
return CodecH265;
}
WarnL << "暂不支持该Amf:" << str;
WarnL << "暂不支持该视频Amf:" << str;
return CodecInvalid;
}
if (val.type() != AMF_NULL){
if (val.type() != AMF_NULL) {
auto type_id = val.as_integer();
switch (type_id){
case FLV_CODEC_H264: return CodecH264;
case FLV_CODEC_AAC: return CodecAAC;
case FLV_CODEC_H265: return CodecH265;
default : WarnL << "暂不支持该Amf:" << type_id; return CodecInvalid;
switch (type_id) {
case FLV_CODEC_H264 : return CodecH264;
case FLV_CODEC_H265 : return CodecH265;
default : WarnL << "暂不支持该视频Amf:" << type_id; return CodecInvalid;
}
}
return CodecInvalid;
}
Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0) {
switch (codecId){
case CodecH264 : return std::make_shared<H264Track>();
case CodecH265 : return std::make_shared<H265Track>();
case CodecAAC : return std::make_shared<AACTrack>();
case CodecOpus: return std::make_shared<OpusTrack>();
case CodecG711A :
case CodecG711U : return (sample_rate && channels && sample_bit) ? std::make_shared<G711Track>(codecId, sample_rate, channels, sample_bit) : nullptr;
default : WarnL << "暂不支持该CodecId:" << codecId; return nullptr;
@@ -191,7 +194,7 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) {
if (str == "mp4a") {
return CodecAAC;
}
WarnL << "暂不支持该Amf:" << str;
WarnL << "暂不支持该音频Amf:" << str;
return CodecInvalid;
}
@@ -201,7 +204,8 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) {
case FLV_CODEC_AAC : return CodecAAC;
case FLV_CODEC_G711A : return CodecG711A;
case FLV_CODEC_G711U : return CodecG711U;
default : WarnL << "暂不支持该Amf:" << type_id; return CodecInvalid;
case FLV_CODEC_OPUS : return CodecOpus;
default : WarnL << "暂不支持该音频Amf:" << type_id; return CodecInvalid;
}
}
@@ -221,6 +225,7 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc
case CodecH264 : return std::make_shared<H264RtmpEncoder>(track);
case CodecAAC : return std::make_shared<AACRtmpEncoder>(track);
case CodecH265 : return std::make_shared<H265RtmpEncoder>(track);
case CodecOpus : return std::make_shared<CommonRtmpEncoder>(track);
case CodecG711A :
case CodecG711U : {
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
@@ -235,7 +240,7 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc
<< ",该音频已被忽略";
return nullptr;
}
return std::make_shared<G711RtmpEncoder>(track);
return std::make_shared<CommonRtmpEncoder>(track);
}
default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr;
}
@@ -248,6 +253,7 @@ AMFValue Factory::getAmfByCodecId(CodecId codecId) {
case CodecH265: return AMFValue(FLV_CODEC_H265);
case CodecG711A: return AMFValue(FLV_CODEC_G711A);
case CodecG711U: return AMFValue(FLV_CODEC_G711U);
case CodecOpus: return AMFValue(FLV_CODEC_OPUS);
default: return AMFValue(AMF_NULL);
}
}

View File

@@ -35,12 +35,12 @@ public:
_dts = frame->dts();
_pts = frame->pts();
_prefix_size = frame->prefixSize();
_codecid = frame->getCodecId();
_codec_id = frame->getCodecId();
_key = frame->keyFrame();
_config = frame->configFrame();
}
virtual ~FrameCacheAble() = default;
~FrameCacheAble() override = default;
/**
* 可以被缓存
@@ -49,10 +49,6 @@ public:
return true;
}
CodecId getCodecId() const override{
return _codecid;
}
bool keyFrame() const override{
return _key;
}
@@ -60,10 +56,10 @@ public:
bool configFrame() const override{
return _config;
}
private:
Frame::Ptr _frame;
BufferRaw::Ptr _buffer;
CodecId _codecid;
bool _key;
bool _config;
};

View File

@@ -148,7 +148,7 @@ public:
}
CodecId getCodecId() const override{
return _codecid;
return _codec_id;
}
bool keyFrame() const override {
@@ -160,7 +160,7 @@ public:
}
public:
CodecId _codecid = CodecInvalid;
CodecId _codec_id = CodecInvalid;
string _buffer;
uint32_t _dts = 0;
uint32_t _pts = 0;
@@ -314,9 +314,24 @@ private:
class FrameFromPtr : public Frame{
public:
typedef std::shared_ptr<FrameFromPtr> Ptr;
FrameFromPtr(CodecId codec_id, char *ptr, uint32_t size, uint32_t dts, uint32_t pts = 0, int prefix_size = 0)
: FrameFromPtr(ptr, size, dts, pts, prefix_size) {
_codec_id = codec_id;
}
FrameFromPtr(char *ptr, uint32_t size, uint32_t dts, uint32_t pts = 0, int prefix_size = 0){
_ptr = ptr;
_size = size;
_dts = dts;
_pts = pts;
_prefix_size = prefix_size;
}
char *data() const override{
return _ptr;
}
uint32_t size() const override {
return _size;
}
@@ -336,12 +351,80 @@ public:
bool cacheAble() const override {
return false;
}
CodecId getCodecId() const override {
if (_codec_id == CodecInvalid) {
throw std::invalid_argument("FrameFromPtr对象未设置codec类型");
}
return _codec_id;
}
void setCodecId(CodecId codec_id) {
_codec_id = codec_id;
}
bool keyFrame() const override {
return false;
}
bool configFrame() const override{
return false;
}
protected:
FrameFromPtr() {}
protected:
char *_ptr;
uint32_t _size;
uint32_t _dts;
uint32_t _pts = 0;
uint32_t _prefix_size;
CodecId _codec_id = CodecInvalid;
};
/**
* 该对象可以把Buffer对象转换成可缓存的Frame对象
*/
template <typename Parent>
class FrameWrapper : public Parent{
public:
~FrameWrapper() = default;
/**
* 构造frame
* @param buf 数据缓存
* @param dts 解码时间戳
* @param pts 显示时间戳
* @param prefix 帧前缀长度
* @param offset buffer有效数据偏移量
*/
FrameWrapper(const Buffer::Ptr &buf, int64_t dts, int64_t pts, int prefix, int offset) : Parent(buf->data() + offset, buf->size() - offset, dts, pts, prefix){
_buf = buf;
}
/**
* 构造frame
* @param buf 数据缓存
* @param dts 解码时间戳
* @param pts 显示时间戳
* @param prefix 帧前缀长度
* @param offset buffer有效数据偏移量
* @param codec 帧类型
*/
FrameWrapper(const Buffer::Ptr &buf, int64_t dts, int64_t pts, int prefix, int offset, CodecId codec) : Parent(codec, buf->data() + offset, buf->size() - offset, dts, pts, prefix){
_buf = buf;
}
/**
* 该帧可缓存
*/
bool cacheAble() const override {
return true;
}
private:
Buffer::Ptr _buf;
};
}//namespace mediakit

View File

@@ -16,47 +16,6 @@
namespace mediakit{
/**
* G711帧
*/
class G711Frame : public FrameImp {
public:
G711Frame(){
_codecid = CodecG711A;
}
};
class G711FrameNoCacheAble : public FrameFromPtr {
public:
typedef std::shared_ptr<G711FrameNoCacheAble> Ptr;
G711FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefix_size = 0){
_ptr = ptr;
_size = size;
_dts = dts;
_prefix_size = prefix_size;
}
void setCodec(CodecId codecId){
_codecId = codecId;
}
CodecId getCodecId() const override{
return _codecId;
}
bool keyFrame() const override {
return false;
}
bool configFrame() const override{
return false;
}
private:
CodecId _codecId;
};
/**
* G711音频通道
*/

View File

@@ -1,63 +0,0 @@
/*
* 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.
*/
#include "G711Rtmp.h"
namespace mediakit{
G711RtmpDecoder::G711RtmpDecoder(CodecId codecId) {
_frame = obtainFrame();
_codecId = codecId;
}
G711Frame::Ptr G711RtmpDecoder::obtainFrame() {
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
auto frame = ResourcePoolHelper<G711Frame>::obtainObj();
frame->_buffer.clear();
frame->_codecid = _codecId;
return frame;
}
bool G711RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) {
//拷贝G711负载
_frame->_buffer.assign(pkt->strBuf.data() + 1, pkt->strBuf.size() - 1);
_frame->_dts = pkt->timeStamp;
//写入环形缓存
RtmpCodec::inputFrame(_frame);
_frame = obtainFrame();
return false;
}
/////////////////////////////////////////////////////////////////////////////////////
G711RtmpEncoder::G711RtmpEncoder(const Track::Ptr &track) : G711RtmpDecoder(track->getCodecId()) {
_audio_flv_flags = getAudioRtmpFlags(track);
}
void G711RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if(!_audio_flv_flags){
return;
}
RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper<RtmpPacket>::obtainObj();
rtmpPkt->strBuf.clear();
//header
rtmpPkt->strBuf.push_back(_audio_flv_flags);
//g711 data
rtmpPkt->strBuf.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
rtmpPkt->bodySize = rtmpPkt->strBuf.size();
rtmpPkt->chunkId = CHUNK_AUDIO;
rtmpPkt->streamId = STREAM_MEDIA;
rtmpPkt->timeStamp = frame->dts();
rtmpPkt->typeId = MSG_AUDIO;
RtmpCodec::inputRtmp(rtmpPkt, false);
}
}//namespace mediakit

View File

@@ -1,66 +0,0 @@
/*
* 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_G711RTMPCODEC_H
#define ZLMEDIAKIT_G711RTMPCODEC_H
#include "Rtmp/RtmpCodec.h"
#include "Extension/Track.h"
#include "Extension/G711.h"
namespace mediakit{
/**
* G711 Rtmp转G711 Frame类
*/
class G711RtmpDecoder : public RtmpCodec , public ResourcePoolHelper<G711Frame> {
public:
typedef std::shared_ptr<G711RtmpDecoder> Ptr;
G711RtmpDecoder(CodecId codecId);
~G711RtmpDecoder() {}
/**
* 输入Rtmp并解码
* @param Rtmp Rtmp数据包
* @param key_pos 此参数内部强制转换为false,请忽略之
*/
bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override;
CodecId getCodecId() const override{
return _codecId;
}
private:
G711Frame::Ptr obtainFrame();
private:
G711Frame::Ptr _frame;
CodecId _codecId;
};
/**
* G711 RTMP打包类
*/
class G711RtmpEncoder : public G711RtmpDecoder , public ResourcePoolHelper<RtmpPacket> {
public:
typedef std::shared_ptr<G711RtmpEncoder> Ptr;
G711RtmpEncoder(const Track::Ptr &track);
~G711RtmpEncoder() {}
/**
* 输入G711 数据
*/
void inputFrame(const Frame::Ptr &frame) override;
private:
uint8_t _audio_flv_flags = 0;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_G711RTMPCODEC_H

View File

@@ -1,92 +0,0 @@
/*
* 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.
*/
#include "G711Rtp.h"
namespace mediakit{
G711RtpDecoder::G711RtpDecoder(CodecId codecid){
_codecid = codecid;
_frame = obtainFrame();
}
G711Frame::Ptr G711RtpDecoder::obtainFrame() {
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
auto frame = ResourcePoolHelper<G711Frame>::obtainObj();
frame->_buffer.clear();
frame->_codecid = _codecid;
frame->_dts = 0;
return frame;
}
bool G711RtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool) {
// 获取rtp数据长度
int length = rtppack->size() - rtppack->offset;
// 获取rtp数据
const char *rtp_packet_buf = rtppack->data() + rtppack->offset;
if (rtppack->timeStamp != _frame->_dts) {
//时间戳变更,清空上一帧
onGetG711(_frame);
}
//追加数据
_frame->_buffer.append(rtp_packet_buf, length);
//赋值时间戳
_frame->_dts = rtppack->timeStamp;
if (rtppack->mark || _frame->_buffer.size() > 10 * 1024) {
//标记为mark时或者内存快溢出时我们认为这是该帧最后一个包
onGetG711(_frame);
}
return false;
}
void G711RtpDecoder::onGetG711(const G711Frame::Ptr &frame) {
if(!frame->_buffer.empty()){
//写入环形缓存
RtpCodec::inputFrame(frame);
_frame = obtainFrame();
}
}
/////////////////////////////////////////////////////////////////////////////////////
G711RtpEncoder::G711RtpEncoder(CodecId codecid, uint32_t ui32Ssrc, uint32_t ui32MtuSize,
uint32_t ui32SampleRate, uint8_t ui8PayloadType, uint8_t ui8Interleaved) :
G711RtpDecoder(codecid),
RtpInfo(ui32Ssrc, ui32MtuSize, ui32SampleRate, ui8PayloadType, ui8Interleaved) {
}
void G711RtpEncoder::inputFrame(const Frame::Ptr &frame) {
GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS);
auto uiStamp = frame->dts();
auto pcData = frame->data() + frame->prefixSize();
auto iLen = frame->size() - frame->prefixSize();
uiStamp %= cycleMS;
char *ptr = (char *) pcData;
int iSize = iLen;
while (iSize > 0) {
if (iSize <= _ui32MtuSize - 20) {
makeG711Rtp(ptr, iSize, true, uiStamp);
break;
}
makeG711Rtp(ptr, _ui32MtuSize - 20, false, uiStamp);
ptr += (_ui32MtuSize - 20);
iSize -= (_ui32MtuSize - 20);
}
}
void G711RtpEncoder::makeG711Rtp(const void *data, unsigned int len, bool mark, uint32_t uiStamp) {
RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), false);
}
}//namespace mediakit

View File

@@ -1,79 +0,0 @@
/*
* 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_G711RTPCODEC_H
#define ZLMEDIAKIT_G711RTPCODEC_H
#include "Rtsp/RtpCodec.h"
#include "Extension/G711.h"
namespace mediakit{
/**
* rtp转G711类
*/
class G711RtpDecoder : public RtpCodec , public ResourcePoolHelper<G711Frame> {
public:
typedef std::shared_ptr<G711RtpDecoder> Ptr;
G711RtpDecoder(CodecId codecid);
~G711RtpDecoder() {}
/**
* 输入rtp并解码
* @param rtp rtp数据包
* @param key_pos 此参数内部强制转换为false,请忽略之
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override;
CodecId getCodecId() const override{
return _codecid;
}
private:
void onGetG711(const G711Frame::Ptr &frame);
G711Frame::Ptr obtainFrame();
private:
G711Frame::Ptr _frame;
CodecId _codecid;
};
/**
* g711 转rtp类
*/
class G711RtpEncoder : public G711RtpDecoder , public RtpInfo {
public:
typedef std::shared_ptr<G711RtpEncoder> Ptr;
/**
* @param ui32Ssrc ssrc
* @param ui32MtuSize mtu 大小
* @param ui32SampleRate 采样率
* @param ui8PayloadType pt类型
* @param ui8Interleaved rtsp interleaved 值
*/
G711RtpEncoder(CodecId codecid,
uint32_t ui32Ssrc,
uint32_t ui32MtuSize,
uint32_t ui32SampleRate,
uint8_t ui8PayloadType = 0,
uint8_t ui8Interleaved = TrackAudio * 2);
~G711RtpEncoder() {}
/**
* @param frame g711数据
*/
void inputFrame(const Frame::Ptr &frame) override;
private:
void makeG711Rtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp);
};
}//namespace mediakit
#endif //ZLMEDIAKIT_G711RTPCODEC_H

View File

@@ -30,14 +30,16 @@ public:
typedef std::shared_ptr<H264Frame> Ptr;
typedef enum {
NAL_SPS = 7,
NAL_PPS = 8,
NAL_IDR = 5,
NAL_SEI = 6,
NAL_SPS = 7,
NAL_PPS = 8,
NAL_AUD = 9,
NAL_B_P = 1,
} NalType;
H264Frame(){
_codecid = CodecH264;
_codec_id = CodecH264;
}
bool keyFrame() const override {
@@ -68,10 +70,7 @@ public:
_dts = dts;
_pts = pts;
_prefix_size = prefix_size;
}
CodecId getCodecId() const override{
return CodecH264;
_codec_id = CodecH264;
}
bool keyFrame() const override {
@@ -182,8 +181,8 @@ public:
*/
void inputFrame(const Frame::Ptr &frame) override{
int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
if(type == H264Frame::NAL_SPS || type == H264Frame::NAL_SEI){
//有些设备会把SPS PPS IDR帧当做一个帧打包所以我们要split一下
if(type != H264Frame::NAL_B_P && type != H264Frame::NAL_IDR){
//非I/B/P帧情况下split一下防止多个帧粘合在一起
splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, int len, int prefix) {
H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame, (char *)ptr, len, prefix);
inputFrame_l(sub_frame);
@@ -227,6 +226,10 @@ private:
VideoTrack::inputFrame(frame);
}
break;
case H264Frame::NAL_AUD:{
//忽略AUD帧;
}
break;
default:
VideoTrack::inputFrame(frame);

View File

@@ -23,10 +23,6 @@ H264Frame::Ptr H264RtmpDecoder::obtainFrame() {
return frame;
}
bool H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) {
return decodeRtmp(rtmp);
}
/**
* 返回不带0x00 00 00 01头的sps
* @return
@@ -39,18 +35,18 @@ static string getH264SPS(const RtmpPacket &thiz) {
if (!thiz.isCfgFrame()) {
return ret;
}
if (thiz.strBuf.size() < 13) {
if (thiz.buffer.size() < 13) {
WarnL << "bad H264 cfg!";
return ret;
}
uint16_t sps_size ;
memcpy(&sps_size, thiz.strBuf.data() + 11,2);
memcpy(&sps_size, thiz.buffer.data() + 11, 2);
sps_size = ntohs(sps_size);
if ((int) thiz.strBuf.size() < 13 + sps_size) {
if ((int) thiz.buffer.size() < 13 + sps_size) {
WarnL << "bad H264 cfg!";
return ret;
}
ret.assign(thiz.strBuf.data() + 13, sps_size);
ret.assign(thiz.buffer.data() + 13, sps_size);
return ret;
}
@@ -66,60 +62,59 @@ static string getH264PPS(const RtmpPacket &thiz) {
if (!thiz.isCfgFrame()) {
return ret;
}
if (thiz.strBuf.size() < 13) {
if (thiz.buffer.size() < 13) {
WarnL << "bad H264 cfg!";
return ret;
}
uint16_t sps_size ;
memcpy(&sps_size,thiz.strBuf.data() + 11,2);
memcpy(&sps_size, thiz.buffer.data() + 11, 2);
sps_size = ntohs(sps_size);
if ((int) thiz.strBuf.size() < 13 + sps_size + 1 + 2) {
if ((int) thiz.buffer.size() < 13 + sps_size + 1 + 2) {
WarnL << "bad H264 cfg!";
return ret;
}
uint16_t pps_size ;
memcpy(&pps_size, thiz.strBuf.data() + 13 + sps_size + 1,2);
memcpy(&pps_size, thiz.buffer.data() + 13 + sps_size + 1, 2);
pps_size = ntohs(pps_size);
if ((int) thiz.strBuf.size() < 13 + sps_size + 1 + 2 + pps_size) {
if ((int) thiz.buffer.size() < 13 + sps_size + 1 + 2 + pps_size) {
WarnL << "bad H264 cfg!";
return ret;
}
ret.assign(thiz.strBuf.data() + 13 + sps_size + 1 + 2, pps_size);
ret.assign(thiz.buffer.data() + 13 + sps_size + 1 + 2, pps_size);
return ret;
}
bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) {
void H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
if (pkt->isCfgFrame()) {
//缓存sps pps后续插入到I帧之前
_sps = getH264SPS(*pkt);
_pps = getH264PPS(*pkt);
onGetH264(_sps.data(), _sps.size(), pkt->timeStamp , pkt->timeStamp);
onGetH264(_pps.data(), _pps.size(), pkt->timeStamp , pkt->timeStamp);
return false;
onGetH264(_sps.data(), _sps.size(), pkt->time_stamp , pkt->time_stamp);
onGetH264(_pps.data(), _pps.size(), pkt->time_stamp , pkt->time_stamp);
return;
}
if (pkt->strBuf.size() > 9) {
uint32_t iTotalLen = pkt->strBuf.size();
if (pkt->buffer.size() > 9) {
uint32_t iTotalLen = pkt->buffer.size();
uint32_t iOffset = 5;
uint8_t *cts_ptr = (uint8_t *) (pkt->strBuf.data() + 2);
uint8_t *cts_ptr = (uint8_t *) (pkt->buffer.data() + 2);
int32_t cts = (((cts_ptr[0] << 16) | (cts_ptr[1] << 8) | (cts_ptr[2])) + 0xff800000) ^ 0xff800000;
auto pts = pkt->timeStamp + cts;
auto pts = pkt->time_stamp + cts;
while(iOffset + 4 < iTotalLen){
uint32_t iFrameLen;
memcpy(&iFrameLen, pkt->strBuf.data() + iOffset, 4);
memcpy(&iFrameLen, pkt->buffer.data() + iOffset, 4);
iFrameLen = ntohl(iFrameLen);
iOffset += 4;
if(iFrameLen + iOffset > iTotalLen){
break;
}
onGetH264(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp , pts);
onGetH264(pkt->buffer.data() + iOffset, iFrameLen, pkt->time_stamp , pts);
iOffset += iFrameLen;
}
}
return pkt->isVideoKeyFrame();
}
inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dts,uint32_t pts) {
@@ -190,8 +185,8 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
}
}
if(_lastPacket && _lastPacket->timeStamp != frame->dts()) {
RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame());
if(_lastPacket && _lastPacket->time_stamp != frame->dts()) {
RtmpCodec::inputRtmp(_lastPacket);
_lastPacket = nullptr;
}
@@ -202,23 +197,23 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
_lastPacket = ResourcePoolHelper<RtmpPacket>::obtainObj();
_lastPacket->strBuf.clear();
_lastPacket->strBuf.push_back(flags);
_lastPacket->strBuf.push_back(!is_config);
_lastPacket->buffer.clear();
_lastPacket->buffer.push_back(flags);
_lastPacket->buffer.push_back(!is_config);
auto cts = frame->pts() - frame->dts();
cts = htonl(cts);
_lastPacket->strBuf.append((char *)&cts + 1, 3);
_lastPacket->buffer.append((char *)&cts + 1, 3);
_lastPacket->chunkId = CHUNK_VIDEO;
_lastPacket->streamId = STREAM_MEDIA;
_lastPacket->timeStamp = frame->dts();
_lastPacket->typeId = MSG_VIDEO;
_lastPacket->chunk_id = CHUNK_VIDEO;
_lastPacket->stream_index = STREAM_MEDIA;
_lastPacket->time_stamp = frame->dts();
_lastPacket->type_id = MSG_VIDEO;
}
auto size = htonl(iLen);
_lastPacket->strBuf.append((char *) &size, 4);
_lastPacket->strBuf.append(pcData, iLen);
_lastPacket->bodySize = _lastPacket->strBuf.size();
_lastPacket->buffer.append((char *) &size, 4);
_lastPacket->buffer.append(pcData, iLen);
_lastPacket->body_size = _lastPacket->buffer.size();
}
void H264RtmpEncoder::makeVideoConfigPkt() {
@@ -227,39 +222,39 @@ void H264RtmpEncoder::makeVideoConfigPkt() {
bool is_config = true;
RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper<RtmpPacket>::obtainObj();
rtmpPkt->strBuf.clear();
rtmpPkt->buffer.clear();
//header
rtmpPkt->strBuf.push_back(flags);
rtmpPkt->strBuf.push_back(!is_config);
rtmpPkt->buffer.push_back(flags);
rtmpPkt->buffer.push_back(!is_config);
//cts
rtmpPkt->strBuf.append("\x0\x0\x0", 3);
rtmpPkt->buffer.append("\x0\x0\x0", 3);
//AVCDecoderConfigurationRecord start
rtmpPkt->strBuf.push_back(1); // version
rtmpPkt->strBuf.push_back(_sps[1]); // profile
rtmpPkt->strBuf.push_back(_sps[2]); // compat
rtmpPkt->strBuf.push_back(_sps[3]); // level
rtmpPkt->strBuf.push_back(0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
rtmpPkt->strBuf.push_back(0xe1); // 3 bits reserved + 5 bits number of sps (00001)
rtmpPkt->buffer.push_back(1); // version
rtmpPkt->buffer.push_back(_sps[1]); // profile
rtmpPkt->buffer.push_back(_sps[2]); // compat
rtmpPkt->buffer.push_back(_sps[3]); // level
rtmpPkt->buffer.push_back(0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
rtmpPkt->buffer.push_back(0xe1); // 3 bits reserved + 5 bits number of sps (00001)
//sps
uint16_t size = _sps.size();
size = htons(size);
rtmpPkt->strBuf.append((char *) &size, 2);
rtmpPkt->strBuf.append(_sps);
rtmpPkt->buffer.append((char *) &size, 2);
rtmpPkt->buffer.append(_sps);
//pps
rtmpPkt->strBuf.push_back(1); // version
rtmpPkt->buffer.push_back(1); // version
size = _pps.size();
size = htons(size);
rtmpPkt->strBuf.append((char *) &size, 2);
rtmpPkt->strBuf.append(_pps);
rtmpPkt->buffer.append((char *) &size, 2);
rtmpPkt->buffer.append(_pps);
rtmpPkt->bodySize = rtmpPkt->strBuf.size();
rtmpPkt->chunkId = CHUNK_VIDEO;
rtmpPkt->streamId = STREAM_MEDIA;
rtmpPkt->timeStamp = 0;
rtmpPkt->typeId = MSG_VIDEO;
RtmpCodec::inputRtmp(rtmpPkt, false);
rtmpPkt->body_size = rtmpPkt->buffer.size();
rtmpPkt->chunk_id = CHUNK_VIDEO;
rtmpPkt->stream_index = STREAM_MEDIA;
rtmpPkt->time_stamp = 0;
rtmpPkt->type_id = MSG_VIDEO;
RtmpCodec::inputRtmp(rtmpPkt);
}
}//namespace mediakit

View File

@@ -32,17 +32,17 @@ public:
/**
* 输入264 Rtmp包
* @param rtmp Rtmp包
* @param key_pos 此参数忽略之
*/
bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override;
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
CodecId getCodecId() const override{
return CodecH264;
}
protected:
bool decodeRtmp(const RtmpPacket::Ptr &Rtmp);
void onGetH264(const char *pcData, int iLen, uint32_t dts,uint32_t pts);
H264Frame::Ptr obtainFrame();
protected:
H264Frame::Ptr _h264frame;
string _sps;

View File

@@ -12,13 +12,6 @@
namespace mediakit{
typedef struct {
unsigned forbidden_zero_bit :1;
unsigned nal_ref_idc :2;
unsigned type :5;
} NALU;
typedef struct {
unsigned S :1;
unsigned E :1;
@@ -26,15 +19,6 @@ typedef struct {
unsigned type :5;
} FU;
static bool MakeNalu(uint8_t in, NALU &nal) {
nal.forbidden_zero_bit = in >> 7;
if (nal.forbidden_zero_bit) {
return false;
}
nal.nal_ref_idc = (in & 0x60) >> 5;
nal.type = in & 0x1f;
return true;
}
static bool MakeFU(uint8_t in, FU &fu) {
fu.S = in >> 7;
fu.E = (in >> 6) & 0x01;
@@ -86,30 +70,28 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
28 FU-A Fragmentation unit 5.8
29 FU-B Fragmentation unit 5.8
30-31 undefined -
*/
const uint8_t *frame = (uint8_t *) rtppack->data() + rtppack->offset;
int length = rtppack->size() - rtppack->offset;
NALU nal;
MakeNalu(*frame, nal);
int nal_type = *frame & 0x1F;
int nal_suffix = *frame & (~0x1F);
if (nal.type >= 0 && nal.type < 24) {
if (nal_type >= 0 && nal_type < 24) {
//a full frame
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
_h264frame->_buffer.append((char *)frame, length);
_h264frame->_buffer.append((char *) frame, length);
_h264frame->_pts = rtppack->timeStamp;
auto key = _h264frame->keyFrame();
onGetH264(_h264frame);
return (key); //i frame
}
switch (nal.type){
switch (nal_type){
case 24:{
// 24 STAP-A 单一时间的组合包
bool haveIDR = false;
auto ptr = frame + 1;
while(true){
while (true) {
int off = ptr - frame;
if (off >= length) {
break;
@@ -121,14 +103,12 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
if (off + len > length) {
break;
}
if(len >= 10){
//过小的帧丢弃
NALU nal;
MakeNalu(ptr[0], nal);
if (len > 0) {
//有有效数据
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
_h264frame->_buffer.append((char *)ptr, len);
_h264frame->_buffer.append((char *) ptr, len);
_h264frame->_pts = rtppack->timeStamp;
if(nal.type == H264Frame::NAL_IDR){
if ((ptr[0] & 0x1F) == H264Frame::NAL_IDR) {
haveIDR = true;
}
onGetH264(_h264frame);
@@ -144,10 +124,9 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
MakeFU(frame[1], fu);
if (fu.S) {
//该帧的第一个rtp包 FU-A start
char tmp = (nal.forbidden_zero_bit << 7 | nal.nal_ref_idc << 5 | fu.type);
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
_h264frame->_buffer.push_back(tmp);
_h264frame->_buffer.append((char *)frame + 2, length - 2);
_h264frame->_buffer.push_back(nal_suffix | fu.type);
_h264frame->_buffer.append((char *) frame + 2, length - 2);
_h264frame->_pts = rtppack->timeStamp;
//该函数return时保存下当前sequence,以便下次对比seq是否连续
_lastSeq = rtppack->sequence;
@@ -163,20 +142,20 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
if (!fu.E) {
//该帧的中间rtp包 FU-A mid
_h264frame->_buffer.append((char *)frame + 2, length - 2);
_h264frame->_buffer.append((char *) frame + 2, length - 2);
//该函数return时保存下当前sequence,以便下次对比seq是否连续
_lastSeq = rtppack->sequence;
return false;
}
//该帧最后一个rtp包 FU-A end
_h264frame->_buffer.append((char *)frame + 2, length - 2);
_h264frame->_buffer.append((char *) frame + 2, length - 2);
_h264frame->_pts = rtppack->timeStamp;
onGetH264(_h264frame);
return false;
}
default:{
default: {
// 29 FU-B 单NAL单元B模式
// 25 STAP-B 单一时间的组合包
// 26 MTAP16 多个时间的组合包
@@ -184,7 +163,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
// 0 udef
// 30 udef
// 31 udef
WarnL << "不支持的rtp类型:" << (int)nal.type << " " << rtppack->sequence;
WarnL << "不支持的rtp类型:" << (int) nal_type << " " << rtppack->sequence;
return false;
}
}
@@ -215,63 +194,62 @@ H264RtpEncoder::H264RtpEncoder(uint32_t ui32Ssrc,
void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS);
auto pcData = frame->data() + frame->prefixSize();
auto uiStamp = frame->pts();
auto iLen = frame->size() - frame->prefixSize();
//获取NALU的5bit 帧类型
unsigned char naluType = H264_TYPE(pcData[0]);
auto ptr = frame->data() + frame->prefixSize();
auto pts = frame->pts() % cycleMS;
auto len = frame->size() - frame->prefixSize();
auto nal_type = H264_TYPE(ptr[0]);
auto max_rtp_size = _ui32MtuSize - 2;
uiStamp %= cycleMS;
int iSize = _ui32MtuSize - 2;
//超过MTU则按照FU-A模式打包
if (iLen > iSize) {
if (len > max_rtp_size) {
//最高位bit为forbidden_zero_bit,
//后面2bit为nal_ref_idc(帧重要程度),00:可以丢,11:不能丢
//末尾5bit为nalu type固定为28(FU-A)
unsigned char f_nri_flags = (*((unsigned char *) pcData) & 0x60) | 28;
unsigned char nal_fu_a = (*((unsigned char *) ptr) & (~0x1F)) | 28;
unsigned char s_e_r_flags;
bool bFirst = true;
bool mark = false;
int nOffset = 1;
while (!mark) {
if (iLen <= nOffset + iSize) {
bool fu_a_start = true;
bool mark_bit = false;
int offset = 1;
while (!mark_bit) {
if (len <= offset + max_rtp_size) {
//已经拆分结束
iSize = iLen - nOffset;
mark = true;
max_rtp_size = len - offset;
mark_bit = true;
//FU-A end
s_e_r_flags = (1 << 6) | naluType;
} else if (bFirst) {
s_e_r_flags = (1 << 6) | nal_type;
} else if (fu_a_start) {
//FU-A start
s_e_r_flags = (1 << 7) | naluType;
s_e_r_flags = (1 << 7) | nal_type;
} else {
//FU-A mid
s_e_r_flags = naluType;
s_e_r_flags = nal_type;
}
{
//传入nullptr先不做payload的内存拷贝
auto rtp = makeRtp(getTrackType(), nullptr, iSize + 2, mark, uiStamp);
auto rtp = makeRtp(getTrackType(), nullptr, max_rtp_size + 2, mark_bit, pts);
//rtp payload 负载部分
uint8_t *payload = (uint8_t*)rtp->data() + rtp->offset;
//FU-A 第1个字节
payload[0] = f_nri_flags;
payload[0] = nal_fu_a;
//FU-A 第2个字节
payload[1] = s_e_r_flags;
//H264 数据
memcpy(payload + 2, (unsigned char *) pcData + nOffset, iSize);
memcpy(payload + 2, (unsigned char *) ptr + offset, max_rtp_size);
//输入到rtp环形缓存
RtpCodec::inputRtp(rtp,bFirst && naluType == H264Frame::NAL_IDR);
RtpCodec::inputRtp(rtp, fu_a_start && nal_type == H264Frame::NAL_IDR);
}
nOffset += iSize;
bFirst = false;
offset += max_rtp_size;
fu_a_start = false;
}
} else {
makeH264Rtp(naluType,pcData, iLen, true, true, uiStamp);
//如果帧长度不超过mtu, 则按照Single NAL unit packet per H.264 方式打包
makeH264Rtp(ptr, len, false, false, pts);
}
}
void H264RtpEncoder::makeH264Rtp(int nal_type,const void* data, unsigned int len, bool mark, bool first_packet, uint32_t uiStamp) {
RtpCodec::inputRtp(makeRtp(getTrackType(),data,len,mark,uiStamp),first_packet && nal_type == H264Frame::NAL_IDR);
void H264RtpEncoder::makeH264Rtp(const void* data, unsigned int len, bool mark, bool gop_pos, uint32_t uiStamp) {
RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), gop_pos);
}
}//namespace mediakit

View File

@@ -78,7 +78,7 @@ public:
*/
void inputFrame(const Frame::Ptr &frame) override;
private:
void makeH264Rtp(int nal_type,const void *pData, unsigned int uiLen, bool bMark, bool first_packet, uint32_t uiStamp);
void makeH264Rtp(const void *pData, unsigned int uiLen, bool bMark, bool gop_pos, uint32_t uiStamp);
};
}//namespace mediakit{

View File

@@ -61,7 +61,7 @@ public:
} NaleType;
H265Frame(){
_codecid = CodecH265;
_codec_id = CodecH265;
}
bool keyFrame() const override {
@@ -92,10 +92,7 @@ public:
_dts = dts;
_pts = pts;
_prefix_size = prefix_size;
}
CodecId getCodecId() const override {
return CodecH265;
_codec_id = CodecH265;
}
bool keyFrame() const override {

View File

@@ -27,10 +27,6 @@ H265Frame::Ptr H265RtmpDecoder::obtainFrame() {
return frame;
}
bool H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) {
return decodeRtmp(rtmp);
}
#ifdef ENABLE_MP4
/**
* 返回不带0x00 00 00 01头的sps
@@ -43,61 +39,60 @@ static bool getH265ConfigFrame(const RtmpPacket &thiz,string &frame) {
if (!thiz.isCfgFrame()) {
return false;
}
if (thiz.strBuf.size() < 6) {
if (thiz.buffer.size() < 6) {
WarnL << "bad H265 cfg!";
return false;
}
auto extra = thiz.strBuf.data() + 5;
auto bytes = thiz.strBuf.size() - 5;
auto extra = thiz.buffer.data() + 5;
auto bytes = thiz.buffer.size() - 5;
struct mpeg4_hevc_t hevc = {0};
if (mpeg4_hevc_decoder_configuration_record_load((uint8_t *) extra, bytes, &hevc) > 0) {
uint8_t config[1024] = {0};
int size = mpeg4_hevc_to_nalu(&hevc, config, sizeof(config));
uint8_t *config = new uint8_t[bytes * 2];
int size = mpeg4_hevc_to_nalu(&hevc, config, bytes * 2);
if (size > 4) {
frame.assign((char *) config + 4, size - 4);
return true;
}
delete [] config;
return size > 4;
}
return false;
}
#endif
bool H265RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) {
void H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
if (pkt->isCfgFrame()) {
#ifdef ENABLE_MP4
string config;
if(getH265ConfigFrame(*pkt,config)){
onGetH265(config.data(), config.size(), pkt->timeStamp , pkt->timeStamp);
onGetH265(config.data(), config.size(), pkt->time_stamp , pkt->time_stamp);
}
#else
WarnL << "请开启MP4相关功能并使能\"ENABLE_MP4\",否则对H265-RTMP支持不完善";
#endif
return false;
return;
}
if (pkt->strBuf.size() > 9) {
uint32_t iTotalLen = pkt->strBuf.size();
if (pkt->buffer.size() > 9) {
uint32_t iTotalLen = pkt->buffer.size();
uint32_t iOffset = 5;
uint8_t *cts_ptr = (uint8_t *) (pkt->strBuf.data() + 2);
uint8_t *cts_ptr = (uint8_t *) (pkt->buffer.data() + 2);
int32_t cts = (((cts_ptr[0] << 16) | (cts_ptr[1] << 8) | (cts_ptr[2])) + 0xff800000) ^ 0xff800000;
auto pts = pkt->timeStamp + cts;
auto pts = pkt->time_stamp + cts;
while(iOffset + 4 < iTotalLen){
uint32_t iFrameLen;
memcpy(&iFrameLen, pkt->strBuf.data() + iOffset, 4);
memcpy(&iFrameLen, pkt->buffer.data() + iOffset, 4);
iFrameLen = ntohl(iFrameLen);
iOffset += 4;
if(iFrameLen + iOffset > iTotalLen){
break;
}
onGetH265(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp , pts);
onGetH265(pkt->buffer.data() + iOffset, iFrameLen, pkt->time_stamp , pts);
iOffset += iFrameLen;
}
}
return pkt->isVideoKeyFrame();
}
inline void H265RtmpDecoder::onGetH265(const char* pcData, int iLen, uint32_t dts,uint32_t pts) {
@@ -176,8 +171,8 @@ void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
return;
}
if(_lastPacket && _lastPacket->timeStamp != frame->dts()) {
RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame());
if(_lastPacket && _lastPacket->time_stamp != frame->dts()) {
RtmpCodec::inputRtmp(_lastPacket);
_lastPacket = nullptr;
}
@@ -188,23 +183,23 @@ void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
_lastPacket = ResourcePoolHelper<RtmpPacket>::obtainObj();
_lastPacket->strBuf.clear();
_lastPacket->strBuf.push_back(flags);
_lastPacket->strBuf.push_back(!is_config);
_lastPacket->buffer.clear();
_lastPacket->buffer.push_back(flags);
_lastPacket->buffer.push_back(!is_config);
auto cts = frame->pts() - frame->dts();
cts = htonl(cts);
_lastPacket->strBuf.append((char *)&cts + 1, 3);
_lastPacket->buffer.append((char *)&cts + 1, 3);
_lastPacket->chunkId = CHUNK_VIDEO;
_lastPacket->streamId = STREAM_MEDIA;
_lastPacket->timeStamp = frame->dts();
_lastPacket->typeId = MSG_VIDEO;
_lastPacket->chunk_id = CHUNK_VIDEO;
_lastPacket->stream_index = STREAM_MEDIA;
_lastPacket->time_stamp = frame->dts();
_lastPacket->type_id = MSG_VIDEO;
}
auto size = htonl(iLen);
_lastPacket->strBuf.append((char *) &size, 4);
_lastPacket->strBuf.append(pcData, iLen);
_lastPacket->bodySize = _lastPacket->strBuf.size();
_lastPacket->buffer.append((char *) &size, 4);
_lastPacket->buffer.append(pcData, iLen);
_lastPacket->body_size = _lastPacket->buffer.size();
}
void H265RtmpEncoder::makeVideoConfigPkt() {
@@ -214,13 +209,13 @@ void H265RtmpEncoder::makeVideoConfigPkt() {
bool is_config = true;
RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper<RtmpPacket>::obtainObj();
rtmpPkt->strBuf.clear();
rtmpPkt->buffer.clear();
//header
rtmpPkt->strBuf.push_back(flags);
rtmpPkt->strBuf.push_back(!is_config);
rtmpPkt->buffer.push_back(flags);
rtmpPkt->buffer.push_back(!is_config);
//cts
rtmpPkt->strBuf.append("\x0\x0\x0", 3);
rtmpPkt->buffer.append("\x0\x0\x0", 3);
struct mpeg4_hevc_t hevc = {0};
string vps_sps_pps = string("\x00\x00\x00\x01", 4) + _vps +
@@ -235,14 +230,14 @@ void H265RtmpEncoder::makeVideoConfigPkt() {
}
//HEVCDecoderConfigurationRecord
rtmpPkt->strBuf.append((char *)extra_data, extra_data_size);
rtmpPkt->buffer.append((char *)extra_data, extra_data_size);
rtmpPkt->bodySize = rtmpPkt->strBuf.size();
rtmpPkt->chunkId = CHUNK_VIDEO;
rtmpPkt->streamId = STREAM_MEDIA;
rtmpPkt->timeStamp = 0;
rtmpPkt->typeId = MSG_VIDEO;
RtmpCodec::inputRtmp(rtmpPkt, false);
rtmpPkt->body_size = rtmpPkt->buffer.size();
rtmpPkt->chunk_id = CHUNK_VIDEO;
rtmpPkt->stream_index = STREAM_MEDIA;
rtmpPkt->time_stamp = 0;
rtmpPkt->type_id = MSG_VIDEO;
RtmpCodec::inputRtmp(rtmpPkt);
#else
WarnL << "请开启MP4相关功能并使能\"ENABLE_MP4\",否则对H265-RTMP支持不完善";
#endif

View File

@@ -32,17 +32,17 @@ public:
/**
* 输入265 Rtmp包
* @param rtmp Rtmp包
* @param key_pos 此参数忽略之
*/
bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override;
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
CodecId getCodecId() const override{
return CodecH265;
}
protected:
bool decodeRtmp(const RtmpPacket::Ptr &Rtmp);
void onGetH265(const char *pcData, int iLen, uint32_t dts,uint32_t pts);
H265Frame::Ptr obtainFrame();
protected:
H265Frame::Ptr _h265frame;
};

View File

@@ -200,7 +200,7 @@ void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) {
bFirst = false;
}
} else {
makeH265Rtp(naluType,pcData, iLen, true, true, uiStamp);
makeH265Rtp(naluType,pcData, iLen, false, true, uiStamp);
}
}

View File

@@ -16,52 +16,13 @@
namespace mediakit{
/**
* Opus帧
*/
class OpusFrame : public FrameImp {
public:
typedef std::shared_ptr<OpusFrame> Ptr;
OpusFrame(){
_codecid = CodecOpus;
}
};
/**
* 不可缓存的Opus帧
*/
class OpusFrameNoCacheAble : public FrameFromPtr {
public:
typedef std::shared_ptr<OpusFrameNoCacheAble> Ptr;
OpusFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefix_size = 0){
_ptr = ptr;
_size = size;
_dts = dts;
_prefix_size = prefix_size;
}
CodecId getCodecId() const override{
return CodecOpus;
}
bool keyFrame() const override {
return false;
}
bool configFrame() const override{
return false;
}
};
/**
* Opus帧音频通道
*/
class OpusTrack : public AudioTrackImp{
public:
typedef std::shared_ptr<OpusTrack> Ptr;
OpusTrack(int sample_rate, int channels, int sample_bit) : AudioTrackImp(CodecOpus,sample_rate,channels,sample_bit){}
OpusTrack() : AudioTrackImp(CodecOpus,48000,2,16){}
private:
//克隆该Track

148
src/FMP4/FMP4MediaSource.h Normal file
View File

@@ -0,0 +1,148 @@
/*
* 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_FMP4MEDIASOURCE_H
#define ZLMEDIAKIT_FMP4MEDIASOURCE_H
#include "Common/MediaSource.h"
using namespace toolkit;
#define FMP4_GOP_SIZE 512
namespace mediakit {
//FMP4直播数据包
class FMP4Packet : public BufferString{
public:
using Ptr = std::shared_ptr<FMP4Packet>;
template<typename ...ARGS>
FMP4Packet(ARGS && ...args) : BufferString(std::forward<ARGS>(args)...) {};
~FMP4Packet() override = default;
public:
uint32_t time_stamp = 0;
};
//FMP4直播合并写策略类
class FMP4FlushPolicy : public FlushPolicy{
public:
FMP4FlushPolicy() = default;
~FMP4FlushPolicy() = default;
uint32_t getStamp(const FMP4Packet::Ptr &packet) {
return packet->time_stamp;
}
};
//FMP4直播源
class FMP4MediaSource : public MediaSource, public RingDelegate<FMP4Packet::Ptr>, public PacketCache<FMP4Packet, FMP4FlushPolicy>{
public:
using Ptr = std::shared_ptr<FMP4MediaSource>;
using RingDataType = std::shared_ptr<List<FMP4Packet::Ptr> >;
using RingType = RingBuffer<RingDataType>;
FMP4MediaSource(const string &vhost,
const string &app,
const string &stream_id,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
~FMP4MediaSource() override = default;
/**
* 获取媒体源的环形缓冲
*/
const RingType::Ptr &getRing() const {
return _ring;
}
/**
* 获取fmp4 init segment
*/
const string &getInitSegment() const{
return _init_segment;
}
/**
* 设置fmp4 init segment
* @param str init segment
*/
void setInitSegment(string str) {
_init_segment = std::move(str);
if (_ring) {
regist();
}
}
/**
* 获取播放器个数
*/
int readerCount() override {
return _ring ? _ring->readerCount() : 0;
}
/**
* 输入FMP4包
* @param packet FMP4包
* @param key 是否为关键帧第一个包
*/
void onWrite(const FMP4Packet::Ptr &packet, bool key) override {
if (!_ring) {
createRing();
}
if (key) {
_have_video = true;
}
PacketCache<FMP4Packet, FMP4FlushPolicy>::inputPacket(true, packet, key);
}
/**
* 情况GOP缓存
*/
void clearCache() override {
PacketCache<FMP4Packet, FMP4FlushPolicy>::clearCache();
_ring->clearCache();
}
private:
void createRing(){
weak_ptr<FMP4MediaSource> weak_self = dynamic_pointer_cast<FMP4MediaSource>(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);
if (!_init_segment.empty()) {
regist();
}
}
/**
* 合并写回调
* @param packet_list 合并写缓存列队
* @param key_pos 是否包含关键帧
*/
void onFlush(std::shared_ptr<List<FMP4Packet::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;
string _init_segment;
RingType::Ptr _ring;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FMP4MEDIASOURCE_H

View File

@@ -0,0 +1,85 @@
/*
* 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_FMP4MEDIASOURCEMUXER_H
#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#include "FMP4MediaSource.h"
#include "Record/MP4Muxer.h"
namespace mediakit {
class FMP4MediaSourceMuxer : public MP4MuxerMemory, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<FMP4MediaSourceMuxer> {
public:
using Ptr = std::shared_ptr<FMP4MediaSourceMuxer>;
FMP4MediaSourceMuxer(const string &vhost,
const string &app,
const string &stream_id) {
_media_src = std::make_shared<FMP4MediaSource>(vhost, app, stream_id);
}
~FMP4MediaSourceMuxer() 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) {
MP4MuxerMemory::inputFrame(frame);
}
}
bool isEnabled() {
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return _clear_cache ? true : _enabled;
}
void onAllTrackReady() {
_media_src->setInitSegment(getInitSegment());
}
protected:
void onSegmentData(const string &string, uint32_t stamp, bool key_frame) override {
if (string.empty()) {
return;
}
FMP4Packet::Ptr packet = std::make_shared<FMP4Packet>(std::move(string));
packet->time_stamp = stamp;
_media_src->onWrite(packet, key_frame);
}
private:
bool _enabled = true;
bool _clear_cache = false;
FMP4MediaSource::Ptr _media_src;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H

View File

@@ -13,7 +13,7 @@ namespace mediakit {
HlsPlayer::HlsPlayer(const EventPoller::Ptr &poller){
_segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); });
_poller = poller ? poller : EventPollerPool::Instance().getPoller();
setPoller(poller ? poller : EventPollerPool::Instance().getPoller());
}
HlsPlayer::~HlsPlayer() {}
@@ -63,6 +63,15 @@ void HlsPlayer::playNextTs(bool force){
std::shared_ptr<Ticker> ticker(new Ticker);
_http_ts_player = std::make_shared<HttpTSPlayer>(getPoller(), false);
_http_ts_player->setOnCreateSocket([weakSelf](const EventPoller::Ptr &poller) {
auto strongSelf = weakSelf.lock();
if (strongSelf) {
return strongSelf->createSocket();
}
return Socket::createSocket(poller, true);
});
_http_ts_player->setOnDisconnect([weakSelf, ticker, ts_duration](const SockException &err) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
@@ -84,6 +93,7 @@ void HlsPlayer::playNextTs(bool force){
}, strongSelf->getPoller()));
}
});
_http_ts_player->setOnPacket([weakSelf](const char *data, uint64_t len) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
@@ -94,9 +104,10 @@ void HlsPlayer::playNextTs(bool force){
});
_http_ts_player->setMethod("GET");
if(!(*this)[kNetAdapter].empty()) {
if (!(*this)[kNetAdapter].empty()) {
_http_ts_player->setNetAdapter((*this)[Client::kNetAdapter]);
}
_http_ts_player->sendRequest(_ts_list.front().url, 2 * _ts_list.front().duration);
_ts_list.pop_front();
}
@@ -254,11 +265,15 @@ void HlsPlayerImp::onAllTrackReady() {
}
void HlsPlayerImp::onPlayResult(const SockException &ex) {
if(ex){
if (ex) {
PlayerImp<HlsPlayer, PlayerBase>::onPlayResult(ex);
}else{
} else {
_frame_cache.clear();
_stamp[TrackAudio].setRelativeStamp(0);
_stamp[TrackVideo].setRelativeStamp(0);
_stamp[TrackAudio].syncTo(_stamp[TrackVideo]);
_ticker.resetTime();
setPlayPosition(0);
weak_ptr<HlsPlayerImp> weakSelf = dynamic_pointer_cast<HlsPlayerImp>(shared_from_this());
//每50毫秒执行一次
_timer = std::make_shared<Timer>(0.05, [weakSelf]() {
@@ -288,25 +303,47 @@ void HlsPlayerImp::inputFrame(const Frame::Ptr &frame) {
//根据时间戳缓存frame
_frame_cache.emplace(dts, Frame::getCacheAbleFrame(frame));
while (!_frame_cache.empty()) {
if (_frame_cache.rbegin()->first - _frame_cache.begin()->first > 30 * 1000) {
//缓存超过30秒强制消费掉
if (getBufferMS() > 30 * 1000) {
//缓存超过30秒强制消费至15秒(减少延时或内存占用)
while (getBufferMS() > 15 * 1000) {
MediaSink::inputFrame(_frame_cache.begin()->second);
_frame_cache.erase(_frame_cache.begin());
continue;
}
//缓存小于30秒
break;
//接着播放缓存中最早的帧
setPlayPosition(_frame_cache.begin()->first);
}
}
int64_t HlsPlayerImp::getPlayPosition(){
return _ticker.elapsedTime() + _ticker_offset;
}
int64_t HlsPlayerImp::getBufferMS(){
if(_frame_cache.empty()){
return 0;
}
return _frame_cache.rbegin()->first - _frame_cache.begin()->first;
}
void HlsPlayerImp::setPlayPosition(int64_t pos){
_ticker.resetTime();
_ticker_offset = pos;
}
void HlsPlayerImp::onTick() {
auto it = _frame_cache.begin();
while (it != _frame_cache.end()) {
if (it->first > _ticker.elapsedTime()) {
if (it->first > getPlayPosition()) {
//这些帧还未到时间播放
break;
}
if (getBufferMS() < 3 * 1000) {
//缓存小于3秒,那么降低定时器消费速度(让剩余的数据在3秒后消费完毕)
//目的是为了防止定时器长时间干等后,数据瞬间消费完毕
setPlayPosition(_frame_cache.begin()->first);
}
//消费掉已经到期的帧
MediaSink::inputFrame(it->second);
it = _frame_cache.erase(it);

View File

@@ -138,13 +138,19 @@ private:
void inputFrame(const Frame::Ptr &frame) override;
void onShutdown(const SockException &ex) override;
void onTick();
int64_t getPlayPosition();
void setPlayPosition(int64_t pos);
int64_t getBufferMS();
private:
TSSegment::onSegment _on_ts;
DecoderImp::Ptr _decoder;
multimap<int64_t, Frame::Ptr> _frame_cache;
Timer::Ptr _timer;
int64_t _ticker_offset = 0;
Ticker _ticker;
Stamp _stamp[2];
Timer::Ptr _timer;
DecoderImp::Ptr _decoder;
TSSegment::onSegment _on_ts;
multimap<int64_t, Frame::Ptr> _frame_cache;
};
}//namespace mediakit

View File

@@ -135,7 +135,7 @@ Buffer::Ptr HttpFileBody::readData(uint32_t size) {
//读到数据了
ret->setSize(iRead);
_offset += iRead;
return std::move(ret);
return ret;
}
//读取文件异常,文件真实长度小于声明长度
_offset = _max_size;
@@ -146,7 +146,7 @@ Buffer::Ptr HttpFileBody::readData(uint32_t size) {
//mmap模式
auto ret = std::make_shared<BufferMmap>(_map_addr,_offset,size);
_offset += size;
return std::move(ret);
return ret;
}
//////////////////////////////////////////////////////////////////

View File

@@ -100,7 +100,7 @@ void HttpClient::onConnect(const SockException &ex) {
}
//先假设http客户端只会接收一点点数据只接受http头节省内存
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
_totalBodySize = 0;
_recvedBodySize = 0;
@@ -157,7 +157,7 @@ int64_t HttpClient::onRecvHeader(const char *data, uint64_t len) {
if(_parser["Transfer-Encoding"] == "chunked"){
//我们认为这种情况下后面应该有大量的数据过来,加大接收缓存提高性能
_sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
//如果Transfer-Encoding字段等于chunked则认为后续的content是不限制长度的
_totalBodySize = -1;
@@ -185,9 +185,9 @@ int64_t HttpClient::onRecvHeader(const char *data, uint64_t len) {
_recvedBodySize = 0;
if(_totalBodySize > 0){
//根据_totalBodySize设置接收缓存大小
_sock->setReadBuffer(std::make_shared<BufferRaw>(MIN(_totalBodySize + 1,256 * 1024)));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(MIN(_totalBodySize + 1,256 * 1024)));
}else{
_sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
}
return -1;

View File

@@ -27,7 +27,7 @@ static int kHlsCookieSecond = 60;
static const string kCookieName = "ZL_COOKIE";
static const string kHlsSuffix = "/hls.m3u8";
class HttpCookieAttachment{
class HttpCookieAttachment {
public:
HttpCookieAttachment() {};
~HttpCookieAttachment() {};
@@ -160,7 +160,7 @@ const string &HttpFileManager::getContentType(const char *name) {
dot = strrchr(name, '.');
static StrCaseMap mapType;
static onceToken token([&]() {
for (unsigned int i = 0; i < sizeof (s_mime_src) / sizeof (s_mime_src[0]); ++i) {
for (unsigned int i = 0; i < sizeof(s_mime_src) / sizeof(s_mime_src[0]); ++i) {
mapType.emplace(s_mime_src[i][0], s_mime_src[i][1]);
}
});
@@ -183,8 +183,8 @@ static string searchIndexFile(const string &dir){
}
set<string> setFile;
while ((pDirent = readdir(pDir)) != NULL) {
static set<const char *,StrCaseCompare> indexSet = {"index.html","index.htm","index"};
if(indexSet.find(pDirent->d_name) != indexSet.end()){
static set<const char *, StrCaseCompare> indexSet = {"index.html", "index.htm", "index"};
if (indexSet.find(pDirent->d_name) != indexSet.end()) {
string ret = pDirent->d_name;
closedir(pDir);
return ret;
@@ -196,16 +196,16 @@ static string searchIndexFile(const string &dir){
static bool makeFolderMenu(const string &httpPath, const string &strFullPath, string &strRet) {
GET_CONFIG(bool, dirMenu, Http::kDirMenu);
if(!dirMenu){
if (!dirMenu) {
//不允许浏览文件夹
return false;
}
string strPathPrefix(strFullPath);
string last_dir_name;
if(strPathPrefix.back() == '/'){
if (strPathPrefix.back() == '/') {
strPathPrefix.pop_back();
}else{
last_dir_name = split(strPathPrefix,"/").back();
} else {
last_dir_name = split(strPathPrefix, "/").back();
}
if (!File::is_dir(strPathPrefix.data())) {
@@ -249,24 +249,24 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st
if (File::is_special_dir(pDirent->d_name)) {
continue;
}
if(pDirent->d_name[0] == '.'){
if (pDirent->d_name[0] == '.') {
continue;
}
setFile.emplace(pDirent->d_name);
}
int i = 0;
for(auto &strFile :setFile ){
for (auto &strFile :setFile) {
string strAbsolutePath = strPathPrefix + "/" + strFile;
bool isDir = File::is_dir(strAbsolutePath.data());
ss << "<li><span>" << i++ << "</span>\t";
ss << "<a href=\"";
if(!last_dir_name.empty()){
if (!last_dir_name.empty()) {
ss << last_dir_name << "/" << strFile;
}else{
} else {
ss << strFile;
}
if(isDir){
if (isDir) {
ss << "/";
}
ss << "\">";
@@ -307,11 +307,16 @@ static bool end_of(const string &str, const string &substr){
//拦截hls的播放请求
static bool emitHlsPlayed(const Parser &parser, const MediaInfo &mediaInfo, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){
//访问的hls.m3u8结尾我们转换成kBroadcastMediaPlayed事件
Broadcast::AuthInvoker mediaAuthInvoker = [invoker](const string &err){
Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) {
//cookie有效期为kHlsCookieSecond
invoker(err,"",kHlsCookieSecond);
invoker(err, "", kHlsCookieSecond);
};
return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,mediaInfo,mediaAuthInvoker,static_cast<SockInfo &>(sender));
bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, mediaInfo, auth_invoker, static_cast<SockInfo &>(sender));
if (!flag) {
//未开启鉴权,那么允许播放
auth_invoker("");
}
return flag;
}
class SockInfoImp : public SockInfo{
@@ -320,23 +325,23 @@ public:
SockInfoImp() = default;
~SockInfoImp() override = default;
string get_local_ip() override{
string get_local_ip() override {
return _local_ip;
}
uint16_t get_local_port() override{
uint16_t get_local_port() override {
return _local_port;
}
string get_peer_ip() override{
string get_peer_ip() override {
return _peer_ip;
}
uint16_t get_peer_port() override{
uint16_t get_peer_port() override {
return _peer_port;
}
string getIdentifier() const override{
string getIdentifier() const override {
return _identifier;
}
@@ -379,7 +384,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
//上次cookie是限定本目录
if (attachment._err_msg.empty()) {
//上次鉴权成功
if(attachment._is_hls){
if (attachment._is_hls) {
//如果播放的是hls那么刷新hls的cookie(获取ts文件也会刷新)
cookie->updateTime();
cookie_from_header = false;
@@ -429,7 +434,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
attachment._err_msg = errMsg;
//记录访问的是否为hls
attachment._is_hls = is_hls;
if(is_hls){
if (is_hls) {
//hls相关信息
attachment._hls_data = std::make_shared<HlsCookieData>(mediaInfo, info);
//hls未查找MediaSource
@@ -437,13 +442,14 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
}
(*cookie)[kCookieName].set<HttpCookieAttachment>(std::move(attachment));
callback(errMsg, cookie);
}else{
} else {
callback(errMsg, nullptr);
}
};
if (is_hls && emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender)) {
if (is_hls) {
//是hls的播放鉴权,拦截之
emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender);
return;
}
@@ -459,15 +465,15 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI
* 发送404 Not Found
*/
static void sendNotFound(const HttpFileManager::invoker &cb) {
GET_CONFIG(string,notFound,Http::kNotFound);
cb("404 Not Found","text/html",StrCaseMap(),std::make_shared<HttpStringBody>(notFound));
GET_CONFIG(string, notFound, Http::kNotFound);
cb("404 Not Found", "text/html", StrCaseMap(), std::make_shared<HttpStringBody>(notFound));
}
/**
* 拼接文件路径
*/
static string pathCat(const string &a, const string &b){
if(a.back() == '/'){
if (a.back() == '/') {
return a + b;
}
return a + '/' + b;
@@ -490,7 +496,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
return;
}
if(is_hls){
if (is_hls) {
//hls那么移除掉后缀获取真实的stream_id并且修改协议为HLS
const_cast<string &>(mediaInfo._schema) = HLS_SCHEMA;
replace(const_cast<string &>(mediaInfo._streamid), kHlsSuffix, "");
@@ -500,7 +506,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
//判断是否有权限访问该文件
canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession , file_exist](const string &errMsg, const HttpServerCookie::Ptr &cookie) {
auto strongSession = weakSession.lock();
if(!strongSession){
if (!strongSession) {
//http客户端已经断开不需要回复
return;
}
@@ -508,6 +514,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
//文件鉴权失败
StrCaseMap headerOut;
if (cookie) {
auto lck = cookie->getLock();
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get<HttpCookieAttachment>()._path);
}
cb("401 Unauthorized", "text/html", headerOut, std::make_shared<HttpStringBody>(errMsg));
@@ -517,11 +524,12 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
auto response_file = [file_exist](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) {
StrCaseMap httpHeader;
if (cookie) {
auto lck = cookie->getLock();
httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get<HttpCookieAttachment>()._path);
}
HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) {
if (cookie && file_exist) {
cookie->getLock();
auto lck = cookie->getLock();
auto is_hls = (*cookie)[kCookieName].get<HttpCookieAttachment>()._is_hls;
if (is_hls) {
(*cookie)[kCookieName].get<HttpCookieAttachment>()._hls_data->addByteUsage(body->remainSize());
@@ -535,26 +543,47 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
if (!is_hls) {
//不是hls,直接回复文件或404
response_file(cookie, cb, strFile, parser);
} else {
//是hls直播判断是否存在
bool have_find_media_src = false;
if(cookie){
have_find_media_src = (*cookie)[kCookieName].get<HttpCookieAttachment>()._have_find_media_source;
if(!have_find_media_src){
(*cookie)[kCookieName].get<HttpCookieAttachment>()._have_find_media_source = true;
}
return;
}
//是hls直播判断HLS直播流是否已经注册
bool have_find_media_src = false;
if (cookie) {
auto lck = cookie->getLock();
have_find_media_src = (*cookie)[kCookieName].get<HttpCookieAttachment>()._have_find_media_source;
if (!have_find_media_src) {
(*cookie)[kCookieName].get<HttpCookieAttachment>()._have_find_media_source = true;
}
if(have_find_media_src){
//之前该cookie已经通过MediaSource::findAsync查找过了所以现在只以文件系统查找结果为准
}
if (have_find_media_src) {
//之前该cookie已经通过MediaSource::findAsync查找过了所以现在只以文件系统查找结果为准
response_file(cookie, cb, strFile, parser);
return;
}
//hls文件不存在我们等待其生成并延后回复
MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) {
if (cookie) {
auto lck = cookie->getLock();
//尝试添加HlsMediaSource的观看人数(HLS是按需生成的这样可以触发HLS文件的生成)
(*cookie)[kCookieName].get<HttpCookieAttachment>()._hls_data->addByteUsage(0);
}
if (src && File::is_file(strFile.data())) {
//流和m3u8文件都存在那么直接返回文件
response_file(cookie, cb, strFile, parser);
return;
}
//hls文件不存在我们等待其生成并延后回复
MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) {
//hls已经生成或者超时后仍未生成那么不管怎么样都返回客户端
auto hls = dynamic_pointer_cast<HlsMediaSource>(src);
if (!hls) {
//流不存在,那么直接返回文件(相当于纯粹的HLS文件服务器,但是会挂起播放器15秒左右(用于等待HLS流的注册))
response_file(cookie, cb, strFile, parser);
return;
}
//流存在但是m3u8文件不存在那么等待生成m3u8文件(HLS源注册后并不会立即生成HLS文件有人观看才会按需生成HLS文件)
hls->waitForFile([response_file, cookie, cb, strFile, parser]() {
response_file(cookie, cb, strFile, parser);
});
}
});
});
}
@@ -563,7 +592,7 @@ static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSe
GET_CONFIG(string, rootPath, Http::kRootPath);
auto ret = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath);
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, ret, static_cast<SockInfo &>(sender));
return std::move(ret);
return ret;
}
/**
@@ -615,13 +644,13 @@ void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const Htt
////////////////////////////////////HttpResponseInvokerImp//////////////////////////////////////
void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const{
if(_lambad){
_lambad(codeOut,headerOut,body);
if (_lambad) {
_lambad(codeOut, headerOut, body);
}
}
void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const{
this->operator()(codeOut,headerOut,std::make_shared<HttpStringBody>(body));
this->operator()(codeOut, headerOut, std::make_shared<HttpStringBody>(body));
}
HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda0 &lambda){
@@ -629,23 +658,23 @@ HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::Htt
}
HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda1 &lambda){
if(!lambda){
if (!lambda) {
_lambad = nullptr;
return;
}
_lambad = [lambda](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body){
_lambad = [lambda](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) {
string str;
if(body && body->remainSize()){
if (body && body->remainSize()) {
str = body->readData(body->remainSize())->toString();
}
lambda(codeOut,headerOut,str);
lambda(codeOut, headerOut, str);
};
}
void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
const StrCaseMap &responseHeader,
const string &filePath) const {
StrCaseMap &httpHeader = const_cast<StrCaseMap&>(responseHeader);
StrCaseMap &httpHeader = const_cast<StrCaseMap &>(responseHeader);
std::shared_ptr<FILE> fp(fopen(filePath.data(), "rb"), [](FILE *fp) {
if (fp) {
fclose(fp);
@@ -654,8 +683,8 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
if (!fp) {
//打开文件失败
GET_CONFIG(string,notFound,Http::kNotFound);
GET_CONFIG(string,charSet,Http::kCharSet);
GET_CONFIG(string, notFound, Http::kNotFound);
GET_CONFIG(string, charSet, Http::kCharSet);
auto strContentType = StrPrinter << "text/html; charset=" << charSet << endl;
httpHeader["Content-Type"] = strContentType;
@@ -665,14 +694,14 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
auto &strRange = const_cast<StrCaseMap &>(requestHeader)["Range"];
int64_t iRangeStart = 0;
int64_t iRangeEnd = 0 ;
int64_t iRangeEnd = 0;
int64_t fileSize = HttpMultiFormBody::fileSize(fp.get());
const char *pcHttpResult = NULL;
if (strRange.size() == 0) {
//全部下载
pcHttpResult = "200 OK";
iRangeEnd = fileSize - 1;
iRangeEnd = fileSize - 1;
} else {
//分节下载
pcHttpResult = "206 Partial Content";

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/FMP4播放器("
<< _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;
}
@@ -132,7 +126,7 @@ void HttpSession::onManager() {
bool HttpSession::checkWebSocket(){
auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"];
if(Sec_WebSocket_Key.empty()){
if (Sec_WebSocket_Key.empty()) {
return false;
}
auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
@@ -141,99 +135,95 @@ bool HttpSession::checkWebSocket(){
headerOut["Upgrade"] = "websocket";
headerOut["Connection"] = "Upgrade";
headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept;
if(!_parser["Sec-WebSocket-Protocol"].empty()){
if (!_parser["Sec-WebSocket-Protocol"].empty()) {
headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
}
auto res_cb = [this,headerOut](){
_flv_over_websocket = true;
sendResponse("101 Switching Protocols",false,nullptr,headerOut,nullptr, true);
auto res_cb = [this, headerOut]() {
_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连接
if(!onWebSocketConnect(_parser)){
sendResponse("501 Not Implemented",true, nullptr, headerOut);
//判断是否为websocket-ts
if (checkLiveStreamTS(res_cb)) {
//这里是websocket-ts直播请求
return true;
}
sendResponse("101 Switching Protocols",false, nullptr,headerOut);
//判断是否为websocket-fmp4
if (checkLiveStreamFMP4(res_cb)) {
//这里是websocket-fmp4直播请求
return true;
}
//这是普通的websocket连接
if (!onWebSocketConnect(_parser)) {
sendResponse("501 Not Implemented", true, nullptr, headerOut);
return true;
}
sendResponse("101 Switching Protocols", false, nullptr, headerOut, nullptr, true);
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){
auto pos = strrchr(_parser.Url().data(),'.');
if(!pos){
//未找到".flv"后缀
return false;
}
if(strcasecmp(pos,".flv") != 0){
//未找到".flv"后缀
bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix, const function<void(const MediaSource::Ptr &src)> &cb){
auto pos = strcasestr(_parser.Url().data(), url_suffix.data());
if (!pos || pos + url_suffix.size() != 1 + &_parser.Url().back()) {
//未找到后缀
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()){
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;
}
@@ -250,34 +240,142 @@ bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
return true;
}
//http-fmp4 链接格式:http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2
bool HttpSession::checkLiveStreamFMP4(const function<void()> &cb){
return checkLiveStream(FMP4_SCHEMA, ".live.mp4", [this, cb](const MediaSource::Ptr &src) {
auto fmp4_src = dynamic_pointer_cast<FMP4MediaSource>(src);
assert(fmp4_src);
if (!cb) {
//找到源发送http头负载后续发送
sendResponse("200 OK", false, HttpFileManager::getContentType(".mp4").data(), KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
}
//直播牺牲延时提升发送性能
setSocketFlags();
onWrite(std::make_shared<BufferString>(fmp4_src->getInitSegment()), true);
weak_ptr<HttpSession> weak_self = dynamic_pointer_cast<HttpSession>(shared_from_this());
_fmp4_reader = fmp4_src->getRing()->attach(getPoller());
_fmp4_reader->setDetachCB([weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
strong_self->shutdown(SockException(Err_shutdown, "fmp4 ring buffer detached"));
});
_fmp4_reader->setReadCB([weak_self](const FMP4MediaSource::RingDataType &fmp4_list) {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
int i = 0;
int size = fmp4_list->size();
fmp4_list->for_each([&](const FMP4Packet::Ptr &ts) {
strong_self->onWrite(ts, ++i == size);
});
});
});
}
//http-ts 链接格式:http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2
bool HttpSession::checkLiveStreamTS(const function<void()> &cb){
return checkLiveStream(TS_SCHEMA, ".live.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, HttpFileManager::getContentType(".ts").data(), KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
}
//直播牺牲延时提升发送性能
setSocketFlags();
weak_ptr<HttpSession> weak_self = dynamic_pointer_cast<HttpSession>(shared_from_this());
_ts_reader = ts_src->getRing()->attach(getPoller());
_ts_reader->setDetachCB([weak_self](){
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
strong_self->shutdown(SockException(Err_shutdown,"ts ring buffer detached"));
});
_ts_reader->setReadCB([weak_self](const TSMediaSource::RingDataType &ts_list) {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
int i = 0;
int size = ts_list->size();
ts_list->for_each([&](const TSPacket::Ptr &ts) {
strong_self->onWrite(ts, ++i == size);
});
});
});
}
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
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, HttpFileManager::getContentType(".flv").data(), 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);
}
void HttpSession::Handle_Req_GET_l(int64_t &content_len, bool sendBody) {
//先看看是否为WebSocket请求
if(checkWebSocket()){
if (checkWebSocket()) {
content_len = -1;
_contentCallBack = [this](const char *data,uint64_t len){
WebSocketSplitter::decode((uint8_t *)data,len);
_contentCallBack = [this](const char *data, uint64_t len) {
WebSocketSplitter::decode((uint8_t *) data, len);
//_contentCallBack是可持续的后面还要处理后续数据
return true;
};
return;
}
if(emitHttpEvent(false)){
if (emitHttpEvent(false)) {
//拦截http api事件
return;
}
if(checkLiveFlvStream()){
if (checkLiveStreamFlv()) {
//拦截http-flv播放器
return;
}
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
if (checkLiveStreamTS()) {
//拦截http-ts播放器
return;
}
if (checkLiveStreamFMP4()) {
//拦截http-fmp4播放器
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) {
@@ -389,7 +487,7 @@ void HttpSession::sendResponse(const char *pcStatus,
const char *pcContentType,
const HttpSession::KeyValue &header,
const HttpBody::Ptr &body,
bool is_http_flv ){
bool no_content_length ){
GET_CONFIG(string,charSet,Http::kCharSet);
GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
@@ -400,7 +498,7 @@ void HttpSession::sendResponse(const char *pcStatus,
size = body->remainSize();
}
if(is_http_flv){
if(no_content_length){
//http-flv直播是Keep-Alive类型
bClose = false;
}else if(size >= INT64_MAX){
@@ -425,7 +523,7 @@ void HttpSession::sendResponse(const char *pcStatus,
headerOut.emplace(kAccessControlAllowCredentials, "true");
}
if(!is_http_flv && size >= 0 && size < INT64_MAX){
if(!no_content_length && size >= 0 && size < INT64_MAX){
//文件长度为固定值,且不是http-flv强制设置Content-Length
headerOut[kContentLength] = to_string(size);
}
@@ -475,7 +573,7 @@ void HttpSession::sendResponse(const char *pcStatus,
//发送http body
AsyncSenderData::Ptr data = std::make_shared<AsyncSenderData>(shared_from_this(),body,bClose);
_sock->setOnFlush([data](){
getSock()->setOnFlush([data](){
return AsyncSender::onSocketFlushed(data);
});
AsyncSender::onSocketFlushed(data);
@@ -542,10 +640,10 @@ void HttpSession::Handle_Req_POST(int64_t &content_len) {
//根据Content-Length设置接收缓存大小
if(totalContentLen > 0){
_sock->setReadBuffer(std::make_shared<BufferRaw>(MIN(totalContentLen + 1,256 * 1024)));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(MIN(totalContentLen + 1,256 * 1024)));
}else{
//不定长度的Content-Length
_sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
}
if(totalContentLen > 0 && totalContentLen < maxReqSize ){
@@ -609,7 +707,7 @@ void HttpSession::setSocketFlags(){
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
if(mergeWriteMS > 0) {
//推流模式下关闭TCP_NODELAY会增加推流端的延时但是服务器性能将提高
SockUtil::setNoDelay(_sock->rawFD(), false);
SockUtil::setNoDelay(getSock()->rawFD(), false);
//播放模式下开启MSG_MORE会增加延时但是能提高发送性能
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
}
@@ -622,29 +720,44 @@ 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{
} else {
WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = WebSocketHeader::BINARY;
header._mask_flag = false;
WebSocketSplitter::encode(header,buffer);
WebSocketSplitter::encode(header, buffer);
}
if(flush){
if (flush) {
//本次刷新缓存后,下次不用刷新缓存
HttpSession::setSendFlushFlag(false);
}
}
void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){
_ui64TotalBytes += buffer->size();
_total_bytes_usage += buffer->size();
send(buffer);
}
void HttpSession::onWebSocketDecodeComplete(const WebSocketHeader &header_in){
WebSocketHeader& header = const_cast<WebSocketHeader&>(header_in);
header._mask_flag = false;
switch (header._opcode) {
case WebSocketHeader::CLOSE: {
encode(header, nullptr);
shutdown(SockException(Err_shutdown, "recv close request from client"));
break;
}
default : break;
}
}
void HttpSession::onDetach() {
shutdown(SockException(Err_shutdown,"rtmp ring buffer detached"));
}

View File

@@ -19,6 +19,8 @@
#include "WebSocketSplitter.h"
#include "HttpCookieManager.h"
#include "HttpFileManager.h"
#include "TS/TSMediaSource.h"
#include "FMP4/FMP4MediaSource.h"
using namespace std;
using namespace toolkit;
@@ -47,6 +49,7 @@ public:
void onError(const SockException &err) override;
void onManager() override;
static string urlDecode(const string &str);
protected:
//FlvMuxer override
void onWrite(const Buffer::Ptr &data, bool flush) override ;
@@ -90,35 +93,49 @@ protected:
* @param buffer websocket协议数据
*/
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override;
/**
* 接收到完整的一个webSocket数据包后回调
* @param header 数据包包头
*/
void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override;
private:
void Handle_Req_GET(int64_t &content_len);
void Handle_Req_GET_l(int64_t &content_len, bool sendBody);
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 checkLiveStreamFMP4(const function<void()> &fmp4_list = nullptr);
bool checkWebSocket();
bool emitHttpEvent(bool doInvoke);
void urlDecode(Parser &parser);
void sendNotFound(bool bClose);
void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr,
const HttpSession::KeyValue &header = HttpSession::KeyValue(),
const HttpBody::Ptr &body = nullptr,bool is_http_flv = false);
const HttpBody::Ptr &body = nullptr, bool no_content_length = false);
//设置socket标志
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;
FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader;
//处理content数据的callback
function<bool (const char *data,uint64_t len) > _contentCallBack;
bool _flv_over_websocket = false;
bool _is_flv_stream = false;
};

View File

@@ -12,9 +12,9 @@
namespace mediakit {
HttpTSPlayer::HttpTSPlayer(const EventPoller::Ptr &poller, bool split_ts){
_segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); });
_poller = poller ? poller : EventPollerPool::Instance().getPoller();
_split_ts = split_ts;
_segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); });
setPoller(poller ? poller : EventPollerPool::Instance().getPoller());
}
HttpTSPlayer::~HttpTSPlayer() {}
@@ -25,8 +25,8 @@ int64_t HttpTSPlayer::onResponseHeader(const string &status, const HttpClient::H
shutdown(SockException(Err_other, StrPrinter << "bad http status code:" + status));
return 0;
}
auto contet_type = const_cast< HttpClient::HttpHeader &>(headers)["Content-Type"];
if (contet_type.find("video/mp2t") == 0 || contet_type.find("video/mpeg") == 0) {
auto content_type = const_cast< HttpClient::HttpHeader &>(headers)["Content-Type"];
if (content_type.find("video/mp2t") == 0 || content_type.find("video/mpeg") == 0) {
_is_ts_content = true;
}

View File

@@ -38,11 +38,10 @@ public:
template<typename ...ArgsType>
ClientTypeImp(ArgsType &&...args): ClientType(std::forward<ArgsType>(args)...){}
~ClientTypeImp() override {};
protected:
/**
* 发送前拦截并打包为websocket协议
* @param buf
* @return
*/
int send(const Buffer::Ptr &buf) override{
if(_beforeSendCB){
@@ -50,6 +49,7 @@ protected:
}
return ClientType::send(buf);
}
/**
* 设置发送数据截取回调函数
* @param cb 截取回调函数
@@ -57,6 +57,7 @@ protected:
void setOnBeforeSendCB(const onBeforeSendCB &cb){
_beforeSendCB = cb;
}
private:
onBeforeSendCB _beforeSendCB;
};
@@ -73,7 +74,7 @@ public:
HttpWsClient(ClientTypeImp<ClientType,DataType> &delegate) : _delegate(delegate){
_Sec_WebSocket_Key = encodeBase64(SHA1::encode_bin(makeRandStr(16, false)));
_poller = delegate.getPoller();
setPoller(delegate.getPoller());
}
~HttpWsClient(){}
@@ -108,6 +109,7 @@ public:
header._mask_flag = true;
WebSocketSplitter::encode(header, nullptr);
}
protected:
//HttpClientImp override
@@ -124,6 +126,8 @@ protected:
if(Sec_WebSocket_Accept == const_cast<HttpHeader &>(headers)["Sec-WebSocket-Accept"]){
//success
onWebSocketException(SockException());
//防止ws服务器返回Content-Length
const_cast<HttpHeader &>(headers).erase("Content-Length");
//后续全是websocket负载数据
return -1;
}
@@ -180,7 +184,6 @@ protected:
/**
* tcp连接结果
* @param ex
*/
void onConnect(const SockException &ex) override{
if(ex){
@@ -194,7 +197,6 @@ protected:
/**
* tcp连接断开
* @param ex
*/
void onErr(const SockException &ex) override{
//tcp断开或者shutdown导致的断开
@@ -208,7 +210,7 @@ protected:
* @param header 数据包头
*/
void onWebSocketDecodeHeader(const WebSocketHeader &header) override{
_payload.clear();
_payload_section.clear();
}
/**
@@ -219,10 +221,9 @@ protected:
* @param recved 已接收数据长度(包含本次数据长度)等于header._payload_len时则接受完毕
*/
void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) override{
_payload.append((char *)ptr,len);
_payload_section.append((char *)ptr,len);
}
/**
* 接收到完整的一个webSocket数据包后回调
* @param header 数据包包头
@@ -238,28 +239,46 @@ protected:
//服务器主动关闭
WebSocketSplitter::encode(header,nullptr);
shutdown(SockException(Err_eof,"websocket server close the connection"));
}
break;
}
case WebSocketHeader::PING:{
//心跳包
header._opcode = WebSocketHeader::PONG;
WebSocketSplitter::encode(header,std::make_shared<BufferString>(std::move(_payload)));
}
WebSocketSplitter::encode(header,std::make_shared<BufferString>(std::move(_payload_section)));
break;
case WebSocketHeader::CONTINUATION:{
}
}
break;
case WebSocketHeader::CONTINUATION:
case WebSocketHeader::TEXT:
case WebSocketHeader::BINARY:{
//接收完毕websocket数据包触发onRecv事件
_delegate.onRecv(std::make_shared<BufferString>(std::move(_payload)));
if (!header._fin) {
//还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出
_payload_cache.append(std::move(_payload_section));
if (_payload_cache.size() < MAX_WS_PACKET) {
//还有内存容量缓存分片数据
break;
}
//分片缓存太大,需要清空
}
//最后一个包
if (_payload_cache.empty()) {
//这个包是唯一个分片
_delegate.onRecv(std::make_shared<WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_section)));
break;
}
//这个包由多个分片组成
_payload_cache.append(std::move(_payload_section));
_delegate.onRecv(std::make_shared<WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_cache)));
_payload_cache.clear();
break;
}
break;
default:
break;
default: break;
}
_payload.clear();
_payload_section.clear();
header._mask_flag = flag;
}
@@ -271,6 +290,7 @@ protected:
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{
HttpClientImp::send(buffer);
}
private:
void onWebSocketException(const SockException &ex){
if(!ex){
@@ -292,7 +312,7 @@ private:
});
//设置sock否则shutdown等接口都无效
_delegate.setSock(HttpClientImp::_sock);
_delegate.setSock(HttpClientImp::getSock());
//触发连接成功事件
_delegate.onConnect(ex);
//拦截websocket数据接收
@@ -319,10 +339,10 @@ private:
string _Sec_WebSocket_Key;
function<void(const char *data, int len)> _onRecv;
ClientTypeImp<ClientType,DataType> &_delegate;
string _payload;
string _payload_section;
string _payload_cache;
};
/**
* Tcp客户端转WebSocket客户端模板
* 通过该模板开发者再不修改TcpClient派生类任何代码的情况下快速实现WebSocket协议的包装
@@ -365,6 +385,7 @@ public:
void startWebSocket(const string &ws_url,float fTimeOutSec = 3){
_wsClient->startWsClient(ws_url,fTimeOutSec);
}
private:
typename HttpWsClient<ClientType,DataType>::Ptr _wsClient;
};

View File

@@ -78,7 +78,6 @@ public:
}
};
/**
* 通过该模板类可以透明化WebSocket协议
* 用户只要实现WebSock协议下的具体业务协议譬如基于WebSocket协议的Rtmp协议等
@@ -107,8 +106,9 @@ public:
void attachServer(const TcpServer &server) override{
HttpSessionType::attachServer(server);
_weakServer = const_cast<TcpServer &>(server).shared_from_this();
_weak_server = const_cast<TcpServer &>(server).shared_from_this();
}
protected:
/**
* websocket客户端连接上事件
@@ -117,12 +117,12 @@ protected:
*/
bool onWebSocketConnect(const Parser &header) override{
//创建websocket session类
_session = _creator(header, *this,HttpSessionType::_sock);
_session = _creator(header, *this,HttpSessionType::getSock());
if(!_session){
//此url不允许创建websocket连接
return false;
}
auto strongServer = _weakServer.lock();
auto strongServer = _weak_server.lock();
if(strongServer){
_session->attachServer(*strongServer);
}
@@ -145,24 +145,20 @@ protected:
//允许websocket客户端
return true;
}
/**
* 开始收到一个webSocket数据包
* @param packet
*/
void onWebSocketDecodeHeader(const WebSocketHeader &packet) override{
//新包,原来的包残余数据清空掉
_remian_data.clear();
_payload_section.clear();
}
/**
* 收到websocket数据包负载
* @param packet
* @param ptr
* @param len
* @param recved
*/
void onWebSocketDecodePayload(const WebSocketHeader &packet,const uint8_t *ptr,uint64_t len,uint64_t recved) override {
_remian_data.append((char *)ptr,len);
_payload_section.append((char *)ptr,len);
}
/**
@@ -177,39 +173,60 @@ protected:
switch (header._opcode){
case WebSocketHeader::CLOSE:{
HttpSessionType::encode(header,nullptr);
}
HttpSessionType::shutdown(SockException(Err_shutdown, "recv close request from client"));
break;
}
case WebSocketHeader::PING:{
header._opcode = WebSocketHeader::PONG;
HttpSessionType::encode(header,std::make_shared<BufferString>(_remian_data));
}
HttpSessionType::encode(header,std::make_shared<BufferString>(_payload_section));
break;
case WebSocketHeader::CONTINUATION:{
}
break;
case WebSocketHeader::CONTINUATION:
case WebSocketHeader::TEXT:
case WebSocketHeader::BINARY:{
_session->onRecv(std::make_shared<BufferString>(_remian_data));
if (!header._fin) {
//还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出
_payload_cache.append(std::move(_payload_section));
if (_payload_cache.size() < MAX_WS_PACKET) {
//还有内存容量缓存分片数据
break;
}
//分片缓存太大,需要清空
}
//最后一个包
if (_payload_cache.empty()) {
//这个包是唯一个分片
_session->onRecv(std::make_shared<WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_section)));
break;
}
//这个包由多个分片组成
_payload_cache.append(std::move(_payload_section));
_session->onRecv(std::make_shared<WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_cache)));
_payload_cache.clear();
break;
}
break;
default:
break;
default: break;
}
_remian_data.clear();
_payload_section.clear();
header._mask_flag = flag;
}
/**
* 发送数据进行websocket协议打包后回调
* @param buffer
* 发送数据进行websocket协议打包后回调
*/
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{
HttpSessionType::send(buffer);
}
private:
string _remian_data;
weak_ptr<TcpServer> _weakServer;
string _payload_cache;
string _payload_section;
weak_ptr<TcpServer> _weak_server;
TcpSession::Ptr _session;
Creator _creator;
};

View File

@@ -16,10 +16,12 @@
#include <vector>
#include <memory>
#include "Network/Buffer.h"
using namespace std;
using namespace toolkit;
//websocket组合包最大不得超过4MB(防止内存爆炸)
#define MAX_WS_PACKET (4 * 1024 * 1024)
namespace mediakit {
class WebSocketHeader {
@@ -44,6 +46,7 @@ public:
CONTROL_RSVF = 0xF
} Type;
public:
WebSocketHeader() : _mask(4){
//获取_mask内部buffer的内存地址该内存是malloc开辟的地址为随机
uint64_t ptr = (uint64_t)(&_mask[0]);
@@ -51,6 +54,7 @@ public:
_mask.assign((uint8_t*)(&ptr), (uint8_t*)(&ptr) + 4);
}
virtual ~WebSocketHeader(){}
public:
bool _fin;
uint8_t _reserved;
@@ -60,6 +64,26 @@ public:
vector<uint8_t > _mask;
};
//websocket协议收到的字符串类型缓存用户协议层获取该数据传输的方式
class WebSocketBuffer : public BufferString {
public:
typedef std::shared_ptr<WebSocketBuffer> Ptr;
template<typename ...ARGS>
WebSocketBuffer(WebSocketHeader::Type headType, bool fin, ARGS &&...args)
: _head_type(headType), _fin(fin), BufferString(std::forward<ARGS>(args)...) {}
~WebSocketBuffer() override {}
WebSocketHeader::Type headType() const { return _head_type; }
bool isFinished() const { return _fin; };
private:
WebSocketHeader::Type _head_type;
bool _fin;
};
class WebSocketSplitter : public WebSocketHeader{
public:
WebSocketSplitter(){}
@@ -80,6 +104,7 @@ public:
* @param buffer 负载数据
*/
void encode(const WebSocketHeader &header,const Buffer::Ptr &buffer);
protected:
/**
* 收到一个webSocket数据包包头后续将继续触发onWebSocketDecodePayload回调
@@ -96,7 +121,6 @@ protected:
*/
virtual void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) {};
/**
* 接收到完整的一个webSocket数据包后回调
* @param header 数据包包头
@@ -109,8 +133,10 @@ protected:
* @param len 数据指针长度
*/
virtual void onWebSocketEncodeData(const Buffer::Ptr &buffer){};
private:
void onPayloadData(uint8_t *data, uint64_t len);
private:
string _remain_data;
int _mask_offset = 0;

View File

@@ -17,31 +17,43 @@ using namespace toolkit;
namespace mediakit {
MediaPlayer::MediaPlayer(const EventPoller::Ptr &poller) {
_poller = poller;
if(!_poller){
_poller = EventPollerPool::Instance().getPoller();
}
_poller = poller ? poller : EventPollerPool::Instance().getPoller();
}
MediaPlayer::~MediaPlayer() {
}
void MediaPlayer::play(const string &strUrl) {
_delegate = PlayerBase::createPlayer(_poller,strUrl);
static void setOnCreateSocket_l(const std::shared_ptr<PlayerBase> &delegate, const Socket::onCreateSocket &cb){
auto helper = dynamic_pointer_cast<SocketHelper>(delegate);
if (helper) {
helper->setOnCreateSocket(cb);
}
}
void MediaPlayer::play(const string &url) {
_delegate = PlayerBase::createPlayer(_poller, url);
assert(_delegate);
setOnCreateSocket_l(_delegate, _on_create_socket);
_delegate->setOnShutdown(_shutdownCB);
_delegate->setOnPlayResult(_playResultCB);
_delegate->setOnResume(_resumeCB);
_delegate->setMediaSouce(_pMediaSrc);
_delegate->mINI::operator=(*this);
_delegate->play(strUrl);
_delegate->play(url);
}
EventPoller::Ptr MediaPlayer::getPoller(){
return _poller;
}
void MediaPlayer::pause(bool bPause) {
void MediaPlayer::setOnCreateSocket(Socket::onCreateSocket cb){
setOnCreateSocket_l(_delegate, cb);
_on_create_socket = std::move(cb);
}
void MediaPlayer::pause(bool pause) {
if (_delegate) {
_delegate->pause(bPause);
_delegate->pause(pause);
}
}

View File

@@ -27,12 +27,15 @@ public:
MediaPlayer(const EventPoller::Ptr &poller = nullptr);
virtual ~MediaPlayer();
void play(const string &strUrl) override;
void pause(bool bPause) override;
void play(const string &url) override;
void pause(bool pause) override;
void teardown() override;
EventPoller::Ptr getPoller();
void setOnCreateSocket(Socket::onCreateSocket cb);
private:
EventPoller::Ptr _poller;
Socket::onCreateSocket _on_create_socket;
};
} /* namespace mediakit */

View File

@@ -105,7 +105,7 @@ vector<Track::Ptr> Demuxer::getTracks(bool trackReady) const {
ret.emplace_back(_audioTrack);
}
}
return std::move(ret);
return ret;
}
float Demuxer::getDuration() const {

View File

@@ -46,31 +46,23 @@ static uint8_t s_mute_adts[] = {0xff, 0xf1, 0x6c, 0x40, 0x2d, 0x3f, 0xfc, 0x00,
#define MUTE_ADTS_DATA_LEN sizeof(s_mute_adts)
#define MUTE_ADTS_DATA_MS 130
PlayerProxy::PlayerProxy(const string &strVhost,
const string &strApp,
const string &strSrc,
bool bEnableRtsp,
bool bEnableRtmp,
bool bEnableHls,
bool bEnableMp4,
int iRetryCount,
const EventPoller::Ptr &poller) : MediaPlayer(poller){
_strVhost = strVhost;
_strApp = strApp;
_strSrc = strSrc;
_bEnableRtsp = bEnableRtsp;
_bEnableRtmp = bEnableRtmp;
_bEnableHls = bEnableHls;
_bEnableMp4 = bEnableMp4;
_iRetryCount = iRetryCount;
PlayerProxy::PlayerProxy(const string &vhost, const string &app, const string &stream_id,
bool enable_hls, bool enable_mp4, int retry_count, const EventPoller::Ptr &poller)
: MediaPlayer(poller) {
_vhost = vhost;
_app = app;
_stream_id = stream_id;
_enable_hls = enable_hls;
_enable_mp4 = enable_mp4;
_retry_count = retry_count;
}
void PlayerProxy::setPlayCallbackOnce(const function<void(const SockException &ex)> &cb){
_playCB = cb;
_on_play = cb;
}
void PlayerProxy::setOnClose(const function<void()> &cb){
_onClose = cb;
_on_close = cb;
}
void PlayerProxy::play(const string &strUrlTmp) {
@@ -82,16 +74,16 @@ void PlayerProxy::play(const string &strUrlTmp) {
return;
}
if(strongSelf->_playCB) {
strongSelf->_playCB(err);
strongSelf->_playCB = nullptr;
if(strongSelf->_on_play) {
strongSelf->_on_play(err);
strongSelf->_on_play = nullptr;
}
if(!err) {
// 播放成功
*piFailedCnt = 0;//连续播放失败次数清0
strongSelf->onPlaySuccess();
}else if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) {
}else if(*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) {
// 播放失败,延时重试播放
strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++);
}
@@ -101,21 +93,21 @@ void PlayerProxy::play(const string &strUrlTmp) {
if(!strongSelf) {
return;
}
if(strongSelf->_mediaMuxer) {
auto tracks = strongSelf->getTracks(false);
if(strongSelf->_muxer) {
auto tracks = strongSelf->MediaPlayer::getTracks(false);
for (auto & track : tracks){
track->delDelegate(strongSelf->_mediaMuxer.get());
track->delDelegate(strongSelf->_muxer.get());
}
GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay);
if (resetWhenRePlay) {
strongSelf->_mediaMuxer.reset();
strongSelf->_muxer.reset();
} else {
strongSelf->_mediaMuxer->resetTracks();
strongSelf->_muxer->resetTracks();
}
}
//播放异常中断,延时重试播放
if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) {
if(*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) {
strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++);
}
});
@@ -125,14 +117,12 @@ void PlayerProxy::play(const string &strUrlTmp) {
if(dynamic_pointer_cast<RtspPlayer>(_delegate)){
//rtsp拉流
GET_CONFIG(bool,directProxy,Rtsp::kDirectProxy);
if(directProxy && _bEnableRtsp){
mediaSource = std::make_shared<RtspMediaSource>(_strVhost,_strApp,_strSrc);
if(directProxy){
mediaSource = std::make_shared<RtspMediaSource>(_vhost, _app, _stream_id);
}
} else if(dynamic_pointer_cast<RtmpPlayer>(_delegate)){
//rtmp拉流
if(_bEnableRtmp){
mediaSource = std::make_shared<RtmpMediaSource>(_strVhost,_strApp,_strSrc);
}
//rtmp拉流,rtmp强制直接代理
mediaSource = std::make_shared<RtmpMediaSource>(_vhost, _app, _stream_id);
}
if(mediaSource){
setMediaSouce(mediaSource);
@@ -143,6 +133,7 @@ void PlayerProxy::play(const string &strUrlTmp) {
PlayerProxy::~PlayerProxy() {
_timer.reset();
}
void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){
auto iDelay = MAX(2 * 1000, MIN(iFailedCnt * 3000, 60*1000));
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
@@ -164,16 +155,17 @@ bool PlayerProxy::close(MediaSource &sender,bool force) {
}
//通知其停止推流
weak_ptr<PlayerProxy> weakSlef = dynamic_pointer_cast<PlayerProxy>(shared_from_this());
getPoller()->async_first([weakSlef]() {
auto stronSelf = weakSlef.lock();
if (stronSelf) {
stronSelf->_mediaMuxer.reset();
stronSelf->setMediaSouce(nullptr);
stronSelf->teardown();
if(stronSelf->_onClose){
stronSelf->_onClose();
}
weak_ptr<PlayerProxy> weakSelf = dynamic_pointer_cast<PlayerProxy>(shared_from_this());
getPoller()->async_first([weakSelf]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->_muxer.reset();
strongSelf->setMediaSouce(nullptr);
strongSelf->teardown();
if (strongSelf->_on_close) {
strongSelf->_on_close();
}
});
WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
@@ -181,7 +173,7 @@ bool PlayerProxy::close(MediaSource &sender,bool force) {
}
int PlayerProxy::totalReaderCount(){
return (_mediaMuxer ? _mediaMuxer->totalReaderCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0);
return (_muxer ? _muxer->totalReaderCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0);
}
int PlayerProxy::totalReaderCount(MediaSource &sender) {
@@ -193,88 +185,90 @@ public:
typedef std::shared_ptr<MuteAudioMaker> Ptr;
MuteAudioMaker(){};
virtual ~MuteAudioMaker(){}
~MuteAudioMaker() override {}
void inputFrame(const Frame::Ptr &frame) override {
if(frame->getTrackType() == TrackVideo){
auto iAudioIndex = frame->dts() / MUTE_ADTS_DATA_MS;
if(_iAudioIndex != iAudioIndex){
_iAudioIndex = iAudioIndex;
auto aacFrame = std::make_shared<AACFrameCacheAble>((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS, 0);
auto audio_idx = frame->dts() / MUTE_ADTS_DATA_MS;
if(_audio_idx != audio_idx){
_audio_idx = audio_idx;
auto aacFrame = std::make_shared<FrameFromStaticPtr>(CodecAAC, (char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _audio_idx * MUTE_ADTS_DATA_MS, 0 ,ADTS_HEADER_LEN);
FrameDispatcher::inputFrame(aacFrame);
}
}
}
private:
class AACFrameCacheAble : public AACFrameNoCacheAble{
class FrameFromStaticPtr : public FrameFromPtr{
public:
template <typename ... ARGS>
AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward<ARGS>(args)...){};
virtual ~AACFrameCacheAble() = default;
FrameFromStaticPtr(ARGS && ...args) : FrameFromPtr(std::forward<ARGS>(args)...) {};
~FrameFromStaticPtr() override = default;
bool cacheAble() const override {
return true;
}
};
private:
int _iAudioIndex = 0;
int _audio_idx = 0;
};
void PlayerProxy::onPlaySuccess() {
GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay);
if (dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc)) {
//rtsp拉流代理
if (resetWhenRePlay || !_mediaMuxer) {
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), false, _bEnableRtmp, _bEnableHls, _bEnableMp4));
if (resetWhenRePlay || !_muxer) {
_muxer.reset(new MultiMediaSourceMuxer(_vhost, _app, _stream_id, getDuration(), false, true, _enable_hls, _enable_mp4));
}
} else if (dynamic_pointer_cast<RtmpMediaSource>(_pMediaSrc)) {
//rtmp拉流代理
if (resetWhenRePlay || !_mediaMuxer) {
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, false, _bEnableHls, _bEnableMp4));
if (resetWhenRePlay || !_muxer) {
_muxer.reset(new MultiMediaSourceMuxer(_vhost, _app, _stream_id, getDuration(), true, false, _enable_hls, _enable_mp4));
}
} else {
//其他拉流代理
if (resetWhenRePlay || !_mediaMuxer) {
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, _bEnableRtmp, _bEnableHls, _bEnableMp4));
if (resetWhenRePlay || !_muxer) {
_muxer.reset(new MultiMediaSourceMuxer(_vhost, _app, _stream_id, getDuration(), true, true, _enable_hls, _enable_mp4));
}
}
_mediaMuxer->setMediaListener(shared_from_this());
_muxer->setMediaListener(shared_from_this());
auto videoTrack = getTrack(TrackVideo,false);
if(videoTrack){
auto videoTrack = getTrack(TrackVideo, false);
if (videoTrack) {
//添加视频
_mediaMuxer->addTrack(videoTrack);
_muxer->addTrack(videoTrack);
//视频数据写入_mediaMuxer
videoTrack->addDelegate(_mediaMuxer);
videoTrack->addDelegate(_muxer);
}
//是否添加静音音频
GET_CONFIG(bool,addMuteAudio,General::kAddMuteAudio);
GET_CONFIG(bool, addMuteAudio, General::kAddMuteAudio);
auto audioTrack = getTrack(TrackAudio, false);
if(audioTrack){
if (audioTrack) {
//添加音频
_mediaMuxer->addTrack(audioTrack);
_muxer->addTrack(audioTrack);
//音频数据写入_mediaMuxer
audioTrack->addDelegate(_mediaMuxer);
}else if(addMuteAudio && videoTrack){
audioTrack->addDelegate(_muxer);
} else if (addMuteAudio && videoTrack) {
//没有音频信息,产生一个静音音频
MuteAudioMaker::Ptr audioMaker = std::make_shared<MuteAudioMaker>();
//videoTrack把数据写入MuteAudioMaker
videoTrack->addDelegate(audioMaker);
//添加一个静音Track至_mediaMuxer
_mediaMuxer->addTrack(std::make_shared<AACTrack>());
_muxer->addTrack(std::make_shared<AACTrack>());
//MuteAudioMaker生成静音音频然后写入_mediaMuxer
audioMaker->addDelegate(_mediaMuxer);
audioMaker->addDelegate(_muxer);
}
//添加完毕所有track防止单track情况下最大等待3秒
_mediaMuxer->addTrackCompleted();
_muxer->addTrackCompleted();
if(_pMediaSrc){
_pMediaSrc->setTrackSource(_mediaMuxer);
if (_pMediaSrc) {
//让_muxer对象拦截一部分事件(比如说录像相关事件)
_pMediaSrc->setListener(_muxer);
}
}
} /* namespace mediakit */

View File

@@ -15,32 +15,22 @@
#include "Common/Device.h"
#include "Player/MediaPlayer.h"
#include "Util/TimeTicker.h"
using namespace std;
using namespace toolkit;
namespace mediakit {
class PlayerProxy :public MediaPlayer,
public std::enable_shared_from_this<PlayerProxy> ,
public MediaSourceEvent{
class PlayerProxy : public MediaPlayer, public MediaSourceEvent, public std::enable_shared_from_this<PlayerProxy> {
public:
typedef std::shared_ptr<PlayerProxy> Ptr;
//如果iRetryCount<0,则一直重试播放;否则重试iRetryCount次数
//如果retry_count<0,则一直重试播放;否则重试retry_count次数
//默认一直重试
PlayerProxy(const string &strVhost,
const string &strApp,
const string &strSrc,
bool bEnableRtsp = true,
bool bEnableRtmp = true,
bool bEnableHls = true,
bool bEnableMp4 = false,
int iRetryCount = -1,
const EventPoller::Ptr &poller = nullptr);
PlayerProxy(const string &vhost, const string &app, const string &stream_id,
bool enable_hls = true, bool enable_mp4 = false,
int retry_count = -1, const EventPoller::Ptr &poller = nullptr);
virtual ~PlayerProxy();
~PlayerProxy() override;
/**
* 设置play结果回调只触发一次在play执行之前有效
@@ -64,27 +54,26 @@ public:
* 获取观看总人数
*/
int totalReaderCount() ;
private:
//MediaSourceEvent override
bool close(MediaSource &sender,bool force) override;
int totalReaderCount(MediaSource &sender) override;
void rePlay(const string &strUrl,int iFailedCnt);
void onPlaySuccess();
private:
bool _bEnableRtsp;
bool _bEnableRtmp;
bool _bEnableHls;
bool _bEnableMp4;
int _iRetryCount;
MultiMediaSourceMuxer::Ptr _mediaMuxer;
string _strVhost;
string _strApp;
string _strSrc;
bool _enable_hls;
bool _enable_mp4;
int _retry_count;
string _vhost;
string _app;
string _stream_id;
Timer::Ptr _timer;
function<void(const SockException &ex)> _playCB;
function<void()> _onClose;
function<void()> _on_close;
function<void(const SockException &ex)> _on_play;
MultiMediaSourceMuxer::Ptr _muxer;
};
} /* namespace mediakit */
#endif /* SRC_DEVICE_PLAYERPROXY_H_ */

View File

@@ -19,34 +19,44 @@ namespace mediakit {
MediaPusher::MediaPusher(const MediaSource::Ptr &src,
const EventPoller::Ptr &poller) {
_src = src;
_poller = poller;
if(!_poller){
_poller = EventPollerPool::Instance().getPoller();
}
_poller = poller ? poller : EventPollerPool::Instance().getPoller();
}
MediaPusher::MediaPusher(const string &schema,
const string &strVhost,
const string &strApp,
const string &strStream,
const string &vhost,
const string &app,
const string &stream,
const EventPoller::Ptr &poller) :
MediaPusher(MediaSource::find(schema,strVhost,strApp,strStream),poller){
MediaPusher(MediaSource::find(schema, vhost, app, stream), poller){
}
MediaPusher::~MediaPusher() {
}
void MediaPusher::publish(const string &strUrl) {
_delegate = PusherBase::createPusher(_poller,_src.lock(),strUrl);
static void setOnCreateSocket_l(const std::shared_ptr<PusherBase> &delegate, const Socket::onCreateSocket &cb){
auto helper = dynamic_pointer_cast<SocketHelper>(delegate);
if (helper) {
helper->setOnCreateSocket(cb);
}
}
void MediaPusher::publish(const string &url) {
_delegate = PusherBase::createPusher(_poller, _src.lock(), url);
assert(_delegate);
setOnCreateSocket_l(_delegate, _on_create_socket);
_delegate->setOnShutdown(_shutdownCB);
_delegate->setOnPublished(_publishCB);
_delegate->mINI::operator=(*this);
_delegate->publish(strUrl);
_delegate->publish(url);
}
EventPoller::Ptr MediaPusher::getPoller(){
return _poller;
}
void MediaPusher::setOnCreateSocket(Socket::onCreateSocket cb){
setOnCreateSocket_l(_delegate, cb);
_on_create_socket = std::move(cb);
}
} /* namespace mediakit */

View File

@@ -24,20 +24,24 @@ public:
typedef std::shared_ptr<MediaPusher> Ptr;
MediaPusher(const string &schema,
const string &strVhost,
const string &strApp,
const string &strStream,
const string &vhost,
const string &app,
const string &stream,
const EventPoller::Ptr &poller = nullptr);
MediaPusher(const MediaSource::Ptr &src,
const EventPoller::Ptr &poller = nullptr);
virtual ~MediaPusher();
void publish(const string &strUrl) override;
void publish(const string &url) override;
EventPoller::Ptr getPoller();
void setOnCreateSocket(Socket::onCreateSocket cb);
private:
std::weak_ptr<MediaSource> _src;
EventPoller::Ptr _poller;
Socket::onCreateSocket _on_create_socket;
};
} /* namespace mediakit */

View File

@@ -32,27 +32,27 @@ void HlsMaker::makeIndexFile(bool eof) {
}
}
auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL;
auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL;
string m3u8;
snprintf(file_content,sizeof(file_content),
"#EXTM3U\n"
"#EXT-X-VERSION:3\n"
"#EXT-X-ALLOW-CACHE:NO\n"
"#EXT-X-TARGETDURATION:%u\n"
"#EXT-X-MEDIA-SEQUENCE:%llu\n",
(maxSegmentDuration + 999) / 1000,
sequence);
snprintf(file_content, sizeof(file_content),
"#EXTM3U\n"
"#EXT-X-VERSION:3\n"
"#EXT-X-ALLOW-CACHE:NO\n"
"#EXT-X-TARGETDURATION:%u\n"
"#EXT-X-MEDIA-SEQUENCE:%llu\n",
(maxSegmentDuration + 999) / 1000,
sequence);
m3u8.assign(file_content);
for (auto &tp : _seg_dur_list) {
snprintf(file_content,sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data());
snprintf(file_content, sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data());
m3u8.append(file_content);
}
if (eof) {
snprintf(file_content,sizeof(file_content),"#EXT-X-ENDLIST\n");
snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n");
m3u8.append(file_content);
}
onWriteHls(m3u8.data(), m3u8.size());
@@ -61,12 +61,15 @@ void HlsMaker::makeIndexFile(bool eof) {
void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet) {
if (data && len) {
if(is_idr_fast_packet){
if (is_idr_fast_packet) {
//尝试切片ts
addNewSegment(timestamp);
}
onWriteSegment((char *) data, len);
//记录上次写入数据时间
_ticker_last_data.resetTime();
if (!_last_file_name.empty()) {
//存在切片才写入ts数据
onWriteSegment((char *) data, len);
_last_timestamp = timestamp;
}
} else {
//resetTracks时触发此逻辑
flushLastSegment(true);
@@ -74,7 +77,7 @@ void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_i
}
void HlsMaker::delOldSegment() {
if(_seg_number == 0){
if (_seg_number == 0) {
//如果设置为保留0个切片则认为是保存为点播
return;
}
@@ -83,15 +86,15 @@ void HlsMaker::delOldSegment() {
_seg_dur_list.pop_front();
}
GET_CONFIG(uint32_t,segRetain,Hls::kSegmentRetain);
GET_CONFIG(uint32_t, segRetain, Hls::kSegmentRetain);
//但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕
if (_file_index > _seg_number + segRetain) {
onDelSegment(_file_index - _seg_number - segRetain - 1);
}
}
void HlsMaker::addNewSegment(uint32_t) {
if(!_last_file_name.empty() && _ticker.elapsedTime() < _seg_duration * 1000){
void HlsMaker::addNewSegment(uint32_t stamp) {
if (!_last_file_name.empty() && stamp - _last_seg_timestamp < _seg_duration * 1000) {
//存在上个切片,并且未到分片时间
return;
}
@@ -100,28 +103,36 @@ void HlsMaker::addNewSegment(uint32_t) {
flushLastSegment(_seg_number == 0);
//新增切片
_last_file_name = onOpenSegment(_file_index++);
//重置切片计时器
_ticker.resetTime();
//记录本次切片的起始时间戳
_last_seg_timestamp = stamp;
}
void HlsMaker::flushLastSegment(bool eof){
if(_last_file_name.empty()){
if (_last_file_name.empty()) {
//不存在上个切片
return;
}
//文件创建到最后一次数据写入的时间即为切片长度
auto seg_dur = _ticker.elapsedTime() - _ticker_last_data.elapsedTime();
if(seg_dur <= 0){
auto seg_dur = _last_timestamp - _last_seg_timestamp;
if (seg_dur <= 0) {
seg_dur = 100;
}
_seg_dur_list.push_back(std::make_tuple(seg_dur, _last_file_name));
_seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name)));
_last_file_name.clear();
delOldSegment();
makeIndexFile(eof);
_last_file_name.clear();
onFlushLastSegment(seg_dur);
}
bool HlsMaker::isLive() {
return _seg_number != 0;
}
void HlsMaker::clear() {
_file_index = 0;
_last_seg_timestamp = 0;
_seg_dur_list.clear();
_last_file_name.clear();
}
}//namespace mediakit

View File

@@ -39,6 +39,17 @@ public:
* @param is_idr_fast_packet 是否为关键帧第一个包
*/
void inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet);
/**
* 是否为直播
*/
bool isLive();
/**
* 清空记录
*/
void clear();
protected:
/**
* 创建ts切片文件回调
@@ -68,15 +79,17 @@ protected:
virtual void onWriteHls(const char *data, int len) = 0;
/**
* 关闭上个ts切片并且写入m3u8索引
* @param eof
* 上一个 ts 切片写入完成, 可在这里进行通知处理
* @param duration_ms 上一个 ts 切片的时长, 单位为毫秒
*/
void flushLastSegment(bool eof = false);
virtual void onFlushLastSegment(uint32_t duration_ms) {};
/**
* 是否为直播
* 关闭上个ts切片并且写入m3u8索引
* @param eof HLS直播是否已结束
*/
bool isLive();
void flushLastSegment(bool eof);
private:
/**
* 生成m3u8文件
@@ -94,12 +107,13 @@ private:
* @param timestamp
*/
void addNewSegment(uint32_t timestamp);
private:
uint32_t _seg_number = 0;
float _seg_duration = 0;
uint32_t _seg_number = 0;
uint32_t _last_timestamp = 0;
uint32_t _last_seg_timestamp = 0;
uint64_t _file_index = 0;
Ticker _ticker;
Ticker _ticker_last_data;
string _last_file_name;
std::deque<tuple<int,string> > _seg_dur_list;
};

View File

@@ -8,9 +8,12 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <ctime>
#include <sys/stat.h>
#include "HlsMakerImp.h"
#include "Util/util.h"
#include "Util/uv_errno.h"
using namespace toolkit;
namespace mediakit {
@@ -24,45 +27,61 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
_path_hls = m3u8_file;
_params = params;
_buf_size = bufSize;
_file_buf.reset(new char[bufSize],[](char *ptr){
_file_buf.reset(new char[bufSize], [](char *ptr) {
delete[] ptr;
});
_info.folder = _path_prefix;
}
HlsMakerImp::~HlsMakerImp() {
clearCache();
}
void HlsMakerImp::clearCache() {
//录制完了
flushLastSegment(true);
if(isLive()){
if (isLive()) {
//hls直播才删除文件
clear();
_file = nullptr;
_segment_file_paths.clear();
File::delete_file(_path_prefix.data());
}
}
string HlsMakerImp::onOpenSegment(int index) {
string segment_name , segment_path;
string segment_name, segment_path;
{
auto strDate = getTimeStr("%Y-%m-%d");
auto strHour = getTimeStr("%H");
auto strTime = getTimeStr("%M-%S");
segment_name = StrPrinter << strDate + "/" + strHour + "/" + strTime << "_" << index << ".ts";
segment_path = _path_prefix + "/" + segment_name;
if(isLive()){
_segment_file_paths.emplace(index,segment_path);
segment_path = _path_prefix + "/" + segment_name;
if (isLive()) {
_segment_file_paths.emplace(index, segment_path);
}
}
_file = makeFile(segment_path, true);
if(!_file){
WarnL << "create file falied," << segment_path << " " << get_uv_errmsg();
//保存本切片的元数据
_info.start_time = ::time(NULL);
_info.file_name = segment_name;
_info.file_path = segment_path;
_info.url = _info.app + "/" + _info.stream + "/" + segment_name;
if (!_file) {
WarnL << "create file failed," << segment_path << " " << get_uv_errmsg();
}
if(_params.empty()){
return std::move(segment_name);
if (_params.empty()) {
return segment_name;
}
return std::move(segment_name + "?" + _params);
return segment_name + "?" + _params;
}
void HlsMakerImp::onDelSegment(int index) {
auto it = _segment_file_paths.find(index);
if(it == _segment_file_paths.end()){
if (it == _segment_file_paths.end()) {
return;
}
File::delete_file(it->second.data());
@@ -77,27 +96,39 @@ void HlsMakerImp::onWriteSegment(const char *data, int len) {
void HlsMakerImp::onWriteHls(const char *data, int len) {
auto hls = makeFile(_path_hls);
if(hls){
fwrite(data,len,1,hls.get());
if (hls) {
fwrite(data, len, 1, hls.get());
hls.reset();
if(_media_src){
_media_src->registHls();
if (_media_src) {
_media_src->registHls(true);
}
} else{
WarnL << "create hls file falied," << _path_hls << " " << get_uv_errmsg();
} else {
WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg();
}
//DebugL << "\r\n" << string(data,len);
}
void HlsMakerImp::onFlushLastSegment(uint32_t duration_ms) {
GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs);
if (broadcastRecordTs) {
//关闭ts文件以便获取正确的文件大小
_file = nullptr;
_info.time_len = duration_ms / 1000.0;
struct stat fileData;
stat(_info.file_path.data(), &fileData);
_info.file_size = fileData.st_size;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordTs, _info);
}
}
std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file,bool setbuf) {
std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file, bool setbuf) {
auto file_buf = _file_buf;
auto ret= shared_ptr<FILE>(File::create_file(file.data(), "wb"), [file_buf](FILE *fp) {
auto ret = shared_ptr<FILE>(File::create_file(file.data(), "wb"), [file_buf](FILE *fp) {
if (fp) {
fclose(fp);
}
});
if(ret && setbuf){
if (ret && setbuf) {
setvbuf(ret.get(), _file_buf.get(), _IOFBF, _buf_size);
}
return ret;
@@ -105,9 +136,12 @@ std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file,bool setbuf) {
void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const string &stream_id) {
_media_src = std::make_shared<HlsMediaSource>(vhost, app, stream_id);
_info.app = app;
_info.stream = stream_id;
_info.vhost = vhost;
}
MediaSource::Ptr HlsMakerImp::getMediaSource() const{
HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {
return _media_src;
}

View File

@@ -16,6 +16,7 @@
#include <stdlib.h>
#include "HlsMaker.h"
#include "HlsMediaSource.h"
using namespace std;
namespace mediakit {
@@ -27,7 +28,8 @@ public:
uint32_t bufSize = 64 * 1024,
float seg_duration = 5,
uint32_t seg_number = 3);
virtual ~HlsMakerImp();
~HlsMakerImp() override;
/**
* 设置媒体信息
@@ -41,23 +43,33 @@ public:
* 获取MediaSource
* @return
*/
MediaSource::Ptr getMediaSource() const;
HlsMediaSource::Ptr getMediaSource() const;
/**
* 清空缓存
*/
void clearCache();
protected:
string onOpenSegment(int index) override ;
void onDelSegment(int index) override;
void onWriteSegment(const char *data, int len) override;
void onWriteHls(const char *data, int len) override;
void onFlushLastSegment(uint32_t duration_ms) override;
private:
std::shared_ptr<FILE> makeFile(const string &file,bool setbuf = false);
private:
HlsMediaSource::Ptr _media_src;
map<int /*index*/,string/*file_path*/> _segment_file_paths;
int _buf_size;
string _params;
string _path_hls;
string _path_prefix;
RecordInfo _info;
std::shared_ptr<FILE> _file;
std::shared_ptr<char> _file_buf;
string _path_prefix;
string _path_hls;
string _params;
int _buf_size;
HlsMediaSource::Ptr _media_src;
map<int /*index*/,string/*file_path*/> _segment_file_paths;
};
}//namespace mediakit

View File

@@ -23,9 +23,7 @@ void HlsCookieData::addReaderCount(){
if(!*_added){
auto src = dynamic_pointer_cast<HlsMediaSource>(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid));
if(src){
src->modifyReaderCount(true);
*_added = true;
_src = src;
_ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller());
auto added = _added;
_ring_reader->setDetachCB([added](){
@@ -38,18 +36,15 @@ void HlsCookieData::addReaderCount(){
HlsCookieData::~HlsCookieData() {
if (*_added) {
auto src = _src.lock();
if (src) {
src->modifyReaderCount(false);
}
uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000;
WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port() << ") "
<< "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid
<< ")断开,耗时(s):" << duration;
GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold);
if (_bytes > iFlowThreshold * 1024) {
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true, static_cast<SockInfo&>(*_sock_info));
uint64_t bytes = _bytes.load();
if (bytes > iFlowThreshold * 1024) {
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, bytes, duration, true, static_cast<SockInfo&>(*_sock_info));
}
}
}

View File

@@ -21,12 +21,8 @@ public:
friend class HlsCookieData;
typedef RingBuffer<string> RingType;
typedef std::shared_ptr<HlsMediaSource> Ptr;
HlsMediaSource(const string &vhost, const string &app, const string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){
_readerCount = 0;
_ring = std::make_shared<RingType>();
}
virtual ~HlsMediaSource() = default;
HlsMediaSource(const string &vhost, const string &app, const string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){}
~HlsMediaSource() override = default;
/**
* 获取媒体源的环形缓冲
@@ -37,41 +33,57 @@ public:
/**
* 获取播放器个数
* @return
*/
int readerCount() override {
return _readerCount.load();
return _ring ? _ring->readerCount() : 0;
}
/**
* 注册hls
* 生成m3u8文件时触发
* @param file_created 是否产生了hls文件
*/
void registHls(){
if(!_registed){
void registHls(bool file_created){
if (!_is_regist) {
_is_regist = true;
weak_ptr<HlsMediaSource> weakSelf = dynamic_pointer_cast<HlsMediaSource>(shared_from_this());
auto lam = [weakSelf](int size) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->onReaderChanged(size);
};
_ring = std::make_shared<RingType>(0, std::move(lam));
onReaderChanged(0);
regist();
_registed = true;
}
}
private:
/**
* 修改观看者个数
* @param add 添加海思删除
*/
void modifyReaderCount(bool add) {
if (add) {
++_readerCount;
if (!file_created) {
//没产生文件
return;
}
if (--_readerCount == 0) {
onNoneReader();
//m3u8文件生成发送给播放器
decltype(_list_cb) copy;
{
lock_guard<mutex> lck(_mtx_cb);
copy.swap(_list_cb);
}
copy.for_each([](const function<void()> &cb) {
cb();
});
}
void waitForFile(function<void()> cb) {
//等待生成m3u8文件
lock_guard<mutex> lck(_mtx_cb);
_list_cb.emplace_back(std::move(cb));
}
private:
atomic_int _readerCount;
bool _registed = false;
bool _is_regist = false;
RingType::Ptr _ring;
mutex _mtx_cb;
List<function<void()> > _list_cb;
};
class HlsCookieData{
@@ -80,13 +92,14 @@ public:
HlsCookieData(const MediaInfo &info, const std::shared_ptr<SockInfo> &sock_info);
~HlsCookieData();
void addByteUsage(uint64_t bytes);
private:
void addReaderCount();
private:
uint64_t _bytes = 0;
atomic<uint64_t> _bytes {0};
MediaInfo _info;
std::shared_ptr<bool> _added;
weak_ptr<HlsMediaSource> _src;
Ticker _ticker;
std::shared_ptr<SockInfo> _sock_info;
HlsMediaSource::RingType::RingReader::Ptr _ring_reader;

View File

@@ -15,37 +15,70 @@
#include "TsMuxer.h"
namespace mediakit {
class HlsRecorder
#if defined(ENABLE_HLS)
: public TsMuxer
#endif
{
class HlsRecorder : public MediaSourceEventInterceptor, public TsMuxer, public std::enable_shared_from_this<HlsRecorder> {
public:
typedef std::shared_ptr<HlsRecorder> Ptr;
HlsRecorder(const string &m3u8_file, const string &params){
GET_CONFIG(uint32_t,hlsNum,Hls::kSegmentNum);
GET_CONFIG(uint32_t,hlsBufSize,Hls::kFileBufSize);
GET_CONFIG(uint32_t,hlsDuration,Hls::kSegmentDuration);
_hls = new HlsMakerImp(m3u8_file,params,hlsBufSize,hlsDuration,hlsNum);
GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum);
GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize);
GET_CONFIG(uint32_t, hlsDuration, Hls::kSegmentDuration);
_hls = std::make_shared<HlsMakerImp>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum);
//清空上次的残余文件
_hls->clearCache();
}
~HlsRecorder(){
delete _hls;
}
void setMediaSource(const string &vhost, const string &app, const string &stream_id){
~HlsRecorder(){}
void setMediaSource(const string &vhost, const string &app, const string &stream_id) {
_hls->setMediaSource(vhost, app, stream_id);
}
MediaSource::Ptr getMediaSource() const{
return _hls->getMediaSource();
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) {
_listener = listener;
_hls->getMediaSource()->setListener(shared_from_this());
//先注册媒体流,后续可以按需生成
_hls->getMediaSource()->registHls(false);
}
#if defined(ENABLE_HLS)
protected:
void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) override {
_hls->inputData((char *)packet,bytes,timestamp, is_idr_fast_packet);
};
#endif
int readerCount() {
return _hls->getMediaSource()->readerCount();
}
void onReaderChanged(MediaSource &sender, int size) override {
//hls保留切片个数为0时代表为hls录制(不删除切片)那么不管有无观看者都一直生成hls
_enabled = _hls->isLive() ? size : true;
if (!size && _hls->isLive()) {
//hls直播时如果无人观看就删除视频缓存目的是为了防止视频跳跃
_clear_cache = true;
}
MediaSourceEventInterceptor::onReaderChanged(sender, size);
}
bool isEnabled() {
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return _clear_cache ? true : _enabled;
}
void inputFrame(const Frame::Ptr &frame) override{
if (_clear_cache) {
_clear_cache = false;
_hls->clearCache();
}
if (_enabled) {
TsMuxer::inputFrame(frame);
}
}
private:
HlsMakerImp *_hls;
void onTs(const void *packet, int bytes, uint32_t timestamp, bool is_idr_fast_packet) override {
_hls->inputData((char *) packet, bytes, timestamp, is_idr_fast_packet);
}
private:
//默认不生成hls文件有播放器时再生成
bool _enabled = false;
bool _clear_cache = false;
std::shared_ptr<HlsMakerImp> _hls;
};
}//namespace mediakit
#endif //HLSRECORDER_H

View File

@@ -13,34 +13,125 @@
#include "Util/File.h"
#include "Util/logger.h"
#include "Common/config.h"
#include "fmp4-writer.h"
using namespace toolkit;
namespace mediakit {
/////////////////////////////////////////////////mp4_writer_t/////////////////////////////////////////////////
struct mp4_writer_t {
int is_fmp4;
union {
fmp4_writer_t *fmp4;
mov_writer_t *mov;
} u;
};
mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags){
mp4_writer_t *mp4 = (mp4_writer_t *) malloc(sizeof(mp4_writer_t));
mp4->is_fmp4 = is_fmp4;
if (is_fmp4) {
mp4->u.fmp4 = fmp4_writer_create(buffer, param, flags);
} else {
mp4->u.mov = mov_writer_create(buffer, param, flags);
}
return mp4;
}
void mp4_writer_destroy(mp4_writer_t* mp4){
if (mp4->is_fmp4) {
fmp4_writer_destroy(mp4->u.fmp4);
} else {
mov_writer_destroy(mp4->u.mov);
}
free(mp4);
}
int mp4_writer_add_audio(mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size){
if (mp4->is_fmp4) {
return fmp4_writer_add_audio(mp4->u.fmp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size);
} else {
return mov_writer_add_audio(mp4->u.mov, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size);
}
}
int mp4_writer_add_video(mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size){
if (mp4->is_fmp4) {
return fmp4_writer_add_video(mp4->u.fmp4, object, width, height, extra_data, extra_data_size);
} else {
return mov_writer_add_video(mp4->u.mov, object, width, height, extra_data, extra_data_size);
}
}
int mp4_writer_add_subtitle(mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size){
if (mp4->is_fmp4) {
return fmp4_writer_add_subtitle(mp4->u.fmp4, object, extra_data, extra_data_size);
} else {
return mov_writer_add_subtitle(mp4->u.mov, object, extra_data, extra_data_size);
}
}
int mp4_writer_write(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags){
if (mp4->is_fmp4) {
return fmp4_writer_write(mp4->u.fmp4, track, data, bytes, pts, dts, flags);
} else {
return mov_writer_write(mp4->u.mov, track, data, bytes, pts, dts, flags);
}
}
int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags, int add_nalu_size){
if (mp4->is_fmp4) {
return fmp4_writer_write_l(mp4->u.fmp4, track, data, bytes, pts, dts, flags, add_nalu_size);
} else {
return mov_writer_write_l(mp4->u.mov, track, data, bytes, pts, dts, flags, add_nalu_size);
}
}
int mp4_writer_save_segment(mp4_writer_t* mp4){
if (mp4->is_fmp4) {
return fmp4_writer_save_segment(mp4->u.fmp4);
} else {
return -1;
}
}
int mp4_writer_init_segment(mp4_writer_t* mp4){
if (mp4->is_fmp4) {
return fmp4_writer_init_segment(mp4->u.fmp4);
} else {
return -1;
}
}
/////////////////////////////////////////////////MP4FileIO/////////////////////////////////////////////////
static struct mov_buffer_t s_io = {
[](void* ctx, void* data, uint64_t bytes) {
MP4File *thiz = (MP4File *)ctx;
return thiz->onRead(data,bytes);
[](void *ctx, void *data, uint64_t bytes) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onRead(data, bytes);
},
[](void* ctx, const void* data, uint64_t bytes){
MP4File *thiz = (MP4File *)ctx;
return thiz->onWrite(data,bytes);
[](void *ctx, const void *data, uint64_t bytes) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onWrite(data, bytes);
},
[](void* ctx, uint64_t offset) {
MP4File *thiz = (MP4File *)ctx;
[](void *ctx, uint64_t offset) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onSeek(offset);
},
[](void* ctx){
MP4File *thiz = (MP4File *)ctx;
[](void *ctx) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onTell();
}
};
MP4File::Writer MP4File::createWriter(){
GET_CONFIG(bool, mp4FastStart, Record::kFastStart);
MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){
Writer writer;
writer.reset(mov_writer_create(&s_io,this,mp4FastStart ? MOV_FLAG_FASTSTART : 0),[](mov_writer_t *ptr){
Ptr self = shared_from_this();
//保存自己的强引用,防止提前释放
writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[self](mp4_writer_t *ptr){
if(ptr){
mov_writer_destroy(ptr);
mp4_writer_destroy(ptr);
}
});
if(!writer){
@@ -49,9 +140,11 @@ MP4File::Writer MP4File::createWriter(){
return writer;
}
MP4File::Reader MP4File::createReader(){
MP4FileIO::Reader MP4FileIO::createReader(){
Reader reader;
reader.reset(mov_reader_create(&s_io,this),[](mov_reader_t *ptr){
Ptr self = shared_from_this();
//保存自己的强引用,防止提前释放
reader.reset(mov_reader_create(&s_io,this),[self](mov_reader_t *ptr){
if(ptr){
mov_reader_destroy(ptr);
}
@@ -62,15 +155,17 @@ MP4File::Reader MP4File::createReader(){
return reader;
}
/////////////////////////////////////////////////////MP4FileDisk/////////////////////////////////////////////////////////
#if defined(_WIN32) || defined(_WIN64)
#define fseek64 _fseeki64
#define ftell64 _ftelli64
#define ftell64 _ftelli64
#else
#define fseek64 fseek
#define ftell64 ftell
#define fseek64 fseek
#define ftell64 ftell
#endif
void MP4File::openFile(const char *file,const char *mode) {
void MP4FileDisk::openFile(const char *file, const char *mode) {
//创建文件
auto fp = File::create_file(file, mode);
if(!fp){
@@ -98,28 +193,74 @@ void MP4File::openFile(const char *file,const char *mode) {
});
}
void MP4File::closeFile() {
void MP4FileDisk::closeFile() {
_file = nullptr;
}
int MP4File::onRead(void *data, uint64_t bytes) {
int MP4FileDisk::onRead(void *data, uint64_t bytes) {
if (bytes == fread(data, 1, bytes, _file.get())){
return 0;
}
return 0 != ferror(_file.get()) ? ferror(_file.get()) : -1 /*EOF*/;
}
int MP4File::onWrite(const void *data, uint64_t bytes) {
int MP4FileDisk::onWrite(const void *data, uint64_t bytes) {
return bytes == fwrite(data, 1, bytes, _file.get()) ? 0 : ferror(_file.get());
}
int MP4File::onSeek(uint64_t offset) {
int MP4FileDisk::onSeek(uint64_t offset) {
return fseek64(_file.get(), offset, SEEK_SET);
}
uint64_t MP4File::onTell() {
uint64_t MP4FileDisk::onTell() {
return ftell64(_file.get());
}
/////////////////////////////////////////////////////MP4FileMemory/////////////////////////////////////////////////////////
string MP4FileMemory::getAndClearMemory(){
string ret;
ret.swap(_memory);
_offset = 0;
return ret;
}
uint64_t MP4FileMemory::fileSize() const{
return _memory.size();
}
uint64_t MP4FileMemory::onTell(){
return _offset;
}
int MP4FileMemory::onSeek(uint64_t offset){
if (offset > _memory.size()) {
return -1;
}
_offset = offset;
return 0;
}
int MP4FileMemory::onRead(void *data, uint64_t bytes){
if (_offset >= _memory.size()) {
//EOF
return -1;
}
bytes = MIN(bytes, _memory.size() - _offset);
memcpy(data, _memory.data(), bytes);
_offset += bytes;
return 0;
}
int MP4FileMemory::onWrite(const void *data, uint64_t bytes){
if (_offset + bytes > _memory.size()) {
//需要扩容
_memory.resize(_offset + bytes);
}
memcpy((uint8_t *) _memory.data() + _offset, data, bytes);
_offset += bytes;
return 0;
}
}//namespace mediakit
#endif //NABLE_MP4RECORD

View File

@@ -23,27 +23,127 @@
using namespace std;
namespace mediakit {
class MP4File {
public:
friend struct mov_buffer_t;
typedef std::shared_ptr<mov_writer_t> Writer;
typedef std::shared_ptr<mov_reader_t> Reader;
MP4File() = default;
virtual ~MP4File() = default;
//以下是fmp4/mov的通用接口简单包装了ireader/media-server的接口
typedef struct mp4_writer_t mp4_writer_t;
mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags);
void mp4_writer_destroy(mp4_writer_t* mp4);
int mp4_writer_add_audio(mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size);
int mp4_writer_add_video(mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size);
int mp4_writer_add_subtitle(mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size);
int mp4_writer_write(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags);
int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags, int add_nalu_size);
int mp4_writer_save_segment(mp4_writer_t* mp4);
int mp4_writer_init_segment(mp4_writer_t* mp4);
Writer createWriter();
Reader createReader();
void openFile(const char *file,const char *mode);
//mp4文件IO的抽象接口类
class MP4FileIO : public std::enable_shared_from_this<MP4FileIO> {
public:
using Ptr = std::shared_ptr<MP4FileIO>;
using Writer = std::shared_ptr<mp4_writer_t>;
using Reader = std::shared_ptr<mov_reader_t>;
MP4FileIO() = default;
virtual ~MP4FileIO() = default;
/**
* 创建mp4复用器
* @param flags 支持0、MOV_FLAG_FASTSTART、MOV_FLAG_SEGMENT
* @param is_fmp4 是否为fmp4还是普通mp4
* @return mp4复用器
*/
virtual Writer createWriter(int flags, bool is_fmp4 = false);
/**
* 创建mp4解复用器
* @return mp4解复用器
*/
virtual Reader createReader();
/**
* 获取文件读写位置
*/
virtual uint64_t onTell() = 0;
/**
* seek至文件某处
* @param offset 文件偏移量
* @return 是否成功(0成功)
*/
virtual int onSeek(uint64_t offset) = 0;
/**
* 从文件读取一定数据
* @param data 数据存放指针
* @param bytes 指针长度
* @return 是否成功(0成功)
*/
virtual int onRead(void *data, uint64_t bytes) = 0;
/**
* 写入文件一定数据
* @param data 数据指针
* @param bytes 数据长度
* @return 是否成功(0成功)
*/
virtual int onWrite(const void *data, uint64_t bytes) = 0;
};
//磁盘MP4文件类
class MP4FileDisk : public MP4FileIO {
public:
using Ptr = std::shared_ptr<MP4FileDisk>;
MP4FileDisk() = default;
~MP4FileDisk() override = default;
/**
* 打开磁盘文件
* @param file 文件路径
* @param mode fopen的方式
*/
void openFile(const char *file, const char *mode);
/**
* 关闭磁盘文件
*/
void closeFile();
int onRead(void* data, uint64_t bytes);
int onWrite(const void* data, uint64_t bytes);
int onSeek( uint64_t offset);
uint64_t onTell();
protected:
uint64_t onTell() override;
int onSeek(uint64_t offset) override;
int onRead(void *data, uint64_t bytes) override;
int onWrite(const void *data, uint64_t bytes) override;
private:
std::shared_ptr<FILE> _file;
};
class MP4FileMemory : public MP4FileIO{
public:
using Ptr = std::shared_ptr<MP4FileMemory>;
MP4FileMemory() = default;
~MP4FileMemory() override = default;
/**
* 获取文件大小
*/
uint64_t fileSize() const;
/**
* 获取并清空文件缓存
*/
string getAndClearMemory();
protected:
uint64_t onTell() override;
int onSeek(uint64_t offset) override;
int onRead(void *data, uint64_t bytes) override;
int onWrite(const void *data, uint64_t bytes) override;
private:
uint64_t _offset = 0;
string _memory;
};
}//namespace mediakit
#endif //NABLE_MP4RECORD
#endif //ZLMEDIAKIT_MP4_H

View File

@@ -15,21 +15,24 @@
#include "Extension/H264.h"
#include "Extension/AAC.h"
#include "Extension/G711.h"
#include "Extension/Opus.h"
using namespace toolkit;
namespace mediakit {
MP4Demuxer::MP4Demuxer(const char *file) {
openFile(file,"rb+");
_mov_reader = createReader();
getAllTracks();
_duration_ms = mov_reader_getduration(_mov_reader.get());
}
MP4Demuxer::MP4Demuxer() {}
MP4Demuxer::~MP4Demuxer() {
_mov_reader = nullptr;
closeFile();
}
void MP4Demuxer::openMP4(const string &file){
openFile(file.data(),"rb+");
_mov_reader = createReader();
getAllTracks();
_duration_ms = mov_reader_getduration(_mov_reader.get());
}
int MP4Demuxer::getAllTracks() {
static mov_reader_trackinfo_t s_on_track = {
[](void *param, uint32_t track, uint8_t object, int width, int height, const void *extra, size_t bytes) {
@@ -119,14 +122,22 @@ void MP4Demuxer::onAudioTrack(uint32_t track_id, uint8_t object, int channel_cou
case MOV_OBJECT_AAC:{
auto audio = std::make_shared<AACTrack>(bytes > 0 ? string((char *)extra,bytes) : "");
_track_to_codec.emplace(track_id, audio);
}
break;
}
case MOV_OBJECT_G711a:
case MOV_OBJECT_G711u:{
auto audio = std::make_shared<G711Track>(object == MOV_OBJECT_G711a ? CodecG711A : CodecG711U, sample_rate, channel_count, bit_per_sample / channel_count );
_track_to_codec.emplace(track_id, audio);
}
break;
}
case MOV_OBJECT_OPUS: {
auto audio = std::make_shared<OpusTrack>();
_track_to_codec.emplace(track_id, audio);
break;
}
default:
WarnL << "不支持该编码类型的MP4,已忽略:" << getObjectName(object);
break;
@@ -149,6 +160,8 @@ struct Context{
BufferRaw::Ptr buffer;
};
#define DATA_OFFSET ADTS_HEADER_LEN
Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) {
keyFrame = false;
eof = false;
@@ -163,9 +176,9 @@ Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) {
static mov_onalloc mov_onalloc = [](void *param, int bytes) -> void * {
Context *ctx = (Context *) param;
ctx->buffer = ctx->thiz->_buffer_pool.obtain();
ctx->buffer->setCapacity(bytes + 1);
ctx->buffer->setSize(bytes);
return ctx->buffer->data();
ctx->buffer->setCapacity(bytes + DATA_OFFSET + 1);
ctx->buffer->setSize(bytes + DATA_OFFSET);
return ctx->buffer->data() + DATA_OFFSET;
};
Context ctx = {this, 0};
@@ -189,59 +202,49 @@ Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) {
}
}
template <typename Parent>
class FrameWrapper : public Parent{
public:
~FrameWrapper() = default;
FrameWrapper(const Buffer::Ptr &buf, int64_t pts, int64_t dts, int prefix) : Parent(buf->data(), buf->size(), dts, pts, prefix){
_buf = buf;
}
bool cacheAble() const override {
return true;
}
private:
Buffer::Ptr _buf;
};
Frame::Ptr MP4Demuxer::makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int64_t pts, int64_t dts) {
auto it = _track_to_codec.find(track_id);
if (it == _track_to_codec.end()) {
return nullptr;
}
auto numBytes = buf->size();
auto pBytes = buf->data();
auto bytes = buf->size() - DATA_OFFSET;
auto data = buf->data() + DATA_OFFSET;
auto codec = it->second->getCodecId();
switch (codec) {
case CodecH264 :
case CodecH265 : {
uint32_t iOffset = 0;
while (iOffset < numBytes) {
uint32_t iFrameLen;
memcpy(&iFrameLen, pBytes + iOffset, 4);
iFrameLen = ntohl(iFrameLen);
if (iFrameLen + iOffset + 4 > numBytes) {
uint32_t offset = 0;
while (offset < bytes) {
uint32_t frame_len;
memcpy(&frame_len, data + offset, 4);
frame_len = ntohl(frame_len);
if (frame_len + offset + 4 > bytes) {
return nullptr;
}
memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
iOffset += (iFrameLen + 4);
memcpy(data + offset, "\x0\x0\x0\x1", 4);
offset += (frame_len + 4);
}
if (codec == CodecH264) {
return std::make_shared<FrameWrapper<H264FrameNoCacheAble> >(buf, pts, dts, 4);
return std::make_shared<FrameWrapper<H264FrameNoCacheAble> >(buf, dts, pts, 4, DATA_OFFSET);
}
return std::make_shared<FrameWrapper<H265FrameNoCacheAble> >(buf, pts, dts, 4);
return std::make_shared<FrameWrapper<H265FrameNoCacheAble> >(buf, dts, pts, 4, DATA_OFFSET);
}
case CodecAAC :
return std::make_shared<FrameWrapper<AACFrameNoCacheAble> >(buf, pts, dts, 0);
case CodecAAC: {
AACTrack::Ptr track = dynamic_pointer_cast<AACTrack>(it->second);
assert(track);
//加上adts头
dumpAacConfig(track->getAacCfg(), buf->size() - DATA_OFFSET, (uint8_t *) buf->data() + (DATA_OFFSET - ADTS_HEADER_LEN), ADTS_HEADER_LEN);
return std::make_shared<FrameWrapper<FrameFromPtr> >(buf, dts, pts, ADTS_HEADER_LEN, DATA_OFFSET - ADTS_HEADER_LEN, codec);
}
case CodecOpus:
case CodecG711A:
case CodecG711U: {
auto frame = std::make_shared<FrameWrapper<G711FrameNoCacheAble> >(buf, pts, dts, 0);
frame->setCodec(codec);
return frame;
return std::make_shared<FrameWrapper<FrameFromPtr> >(buf, dts, pts, 0, DATA_OFFSET, codec);
}
default:
return nullptr;
default: return nullptr;
}
}
@@ -253,7 +256,7 @@ vector<Track::Ptr> MP4Demuxer::getTracks(bool trackReady) const {
}
ret.push_back(pr.second);
}
return std::move(ret);
return ret;
}
uint64_t MP4Demuxer::getDurationMS() const {

View File

@@ -16,24 +16,60 @@
#include "Util/ResourcePool.h"
namespace mediakit {
class MP4Demuxer : public MP4File, public TrackSource{
class MP4Demuxer : public MP4FileDisk, public TrackSource{
public:
typedef std::shared_ptr<MP4Demuxer> Ptr;
MP4Demuxer(const char *file);
/**
* 创建mp4解复用器
*/
MP4Demuxer();
~MP4Demuxer() override;
/**
* 打开文件
* @param file mp4文件路径
*/
void openMP4(const string &file);
/**
* 移动时间轴至某处
* @param stamp_ms 预期的时间轴位置,单位毫秒
* @return 时间轴位置
*/
int64_t seekTo(int64_t stamp_ms);
/**
* 读取一帧数据
* @param keyFrame 是否为关键帧
* @param eof 是否文件读取完毕
* @return 帧数据,可能为空
*/
Frame::Ptr readFrame(bool &keyFrame, bool &eof);
vector<Track::Ptr> getTracks(bool trackReady) const override ;
/**
* 获取所有Track信息
* @param trackReady 是否要求track为就绪状态
* @return 所有Track
*/
vector<Track::Ptr> getTracks(bool trackReady) const override;
/**
* 获取文件长度
* @return 文件长度,单位毫秒
*/
uint64_t getDurationMS() const;
private:
int getAllTracks();
void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void* extra, size_t bytes);
void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes);
Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf,int64_t pts, int64_t dts);
void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void *extra, size_t bytes);
void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void *extra, size_t bytes);
Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int64_t pts, int64_t dts);
private:
MP4File::Reader _mov_reader;
Reader _mov_reader;
uint64_t _duration_ms = 0;
map<int, Track::Ptr > _track_to_codec;
map<int, Track::Ptr> _track_to_codec;
ResourcePool<BufferRaw> _buffer_pool;
};

View File

@@ -14,33 +14,57 @@
#include "Extension/H264.h"
namespace mediakit{
MP4Muxer::MP4Muxer(const char *file) {
_file_name = file;
openMP4();
}
MP4Muxer::MP4Muxer() {}
MP4Muxer::~MP4Muxer() {
closeMP4();
}
void MP4Muxer::openMP4(){
void MP4Muxer::openMP4(const string &file){
closeMP4();
openFile(_file_name.data(), "wb+");
_mov_writter = createWriter();
_file_name = file;
_mp4_file = std::make_shared<MP4FileDisk>();
_mp4_file->openFile(_file_name.data(), "wb+");
}
MP4FileIO::Writer MP4Muxer::createWriter(){
GET_CONFIG(bool, mp4FastStart, Record::kFastStart);
return _mp4_file->createWriter(mp4FastStart ? MOV_FLAG_FASTSTART : 0, false);
}
void MP4Muxer::closeMP4(){
_mov_writter = nullptr;
closeFile();
MP4MuxerInterface::resetTracks();
_mp4_file = nullptr;
}
void MP4Muxer::resetTracks() {
_codec_to_trackid.clear();
_started = false;
_have_video = false;
openMP4();
MP4MuxerInterface::resetTracks();
openMP4(_file_name);
}
void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
/////////////////////////////////////////// MP4MuxerInterface /////////////////////////////////////////////
void MP4MuxerInterface::saveSegment(){
mp4_writer_save_segment(_mov_writter.get());
}
void MP4MuxerInterface::initSegment(){
mp4_writer_init_segment(_mov_writter.get());
}
bool MP4MuxerInterface::haveVideo() const{
return _have_video;
}
void MP4MuxerInterface::resetTracks() {
_started = false;
_have_video = false;
_mov_writter = nullptr;
_frameCached.clear();
_codec_to_trackid.clear();
}
void MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if(it == _codec_to_trackid.end()){
//该Track不存在或初始化失败
@@ -48,17 +72,13 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
}
if (!_started) {
//还没开始
if (!_have_video) {
_started = true;
} else {
if (frame->getTrackType() != TrackVideo || !frame->keyFrame()) {
//如果首帧是音频或者是视频但是不是i帧那么不能开始写文件
return;
}
//开始写文件
_started = true;
//该逻辑确保含有视频时,第一帧为关键帧
if (_have_video && !frame->keyFrame()) {
//含有视频,但是不是关键帧,那么前面的帧丢弃
return;
}
//开始写文件
_started = true;
}
//mp4文件时间戳需要从0开始
@@ -88,24 +108,23 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
merged.append((char *) &nalu_size, 4);
merged.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
});
mov_writer_write_l(_mov_writter.get(),
mp4_writer_write(_mov_writter.get(),
track_info.track_id,
merged.data(),
merged.size(),
pts_out,
dts_out,
back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
1/*我们合并时已经生成了4个字节的MP4格式start code*/);
back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0);
} else {
//缓存中只有一帧视频
mov_writer_write_l(_mov_writter.get(),
mp4_writer_write_l(_mov_writter.get(),
track_info.track_id,
back->data() + back->prefixSize(),
back->size() - back->prefixSize(),
pts_out,
dts_out,
back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
0/*需要生成头4个字节的MP4格式start code*/);
1/*需要生成头4个字节的MP4格式start code*/);
}
_frameCached.clear();
}
@@ -115,14 +134,13 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
break;
default: {
track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
mov_writer_write_l(_mov_writter.get(),
track_info.track_id,
frame->data() + frame->prefixSize(),
frame->size() - frame->prefixSize(),
pts_out,
dts_out,
frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
1/*aac或其他类型frame不用添加4个nalu_size的字节*/);
mp4_writer_write(_mov_writter.get(),
track_info.track_id,
frame->data() + frame->prefixSize(),
frame->size() - frame->prefixSize(),
pts_out,
dts_out,
frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0);
}
break;
}
@@ -140,7 +158,7 @@ static uint8_t getObject(CodecId codecId){
}
}
void MP4Muxer::stampSync(){
void MP4MuxerInterface::stampSync(){
if(_codec_to_trackid.size() < 2){
return;
}
@@ -160,7 +178,10 @@ void MP4Muxer::stampSync(){
}
}
void MP4Muxer::addTrack(const Track::Ptr &track) {
void MP4MuxerInterface::addTrack(const Track::Ptr &track) {
if (!_mov_writter) {
_mov_writter = createWriter();
}
auto mp4_object = getObject(track->getCodecId());
if (!mp4_object) {
WarnL << "MP4录制不支持该编码格式:" << track->getCodecName();
@@ -182,7 +203,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_audio(_mov_writter.get(),
auto track_id = mp4_writer_add_audio(_mov_writter.get(),
mp4_object,
audio_track->getAudioChannel(),
audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
@@ -203,7 +224,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_audio(_mov_writter.get(),
auto track_id = mp4_writer_add_audio(_mov_writter.get(),
mp4_object,
audio_track->getAudioChannel(),
audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
@@ -236,7 +257,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_video(_mov_writter.get(),
auto track_id = mp4_writer_add_video(_mov_writter.get(),
mp4_object,
h264_track->getVideoWidth(),
h264_track->getVideoHeight(),
@@ -271,7 +292,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_video(_mov_writter.get(),
auto track_id = mp4_writer_add_video(_mov_writter.get(),
mp4_object,
h265_track->getVideoWidth(),
h265_track->getVideoHeight(),
@@ -293,5 +314,54 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
stampSync();
}
/////////////////////////////////////////// MP4MuxerMemory /////////////////////////////////////////////
MP4MuxerMemory::MP4MuxerMemory() {
_memory_file = std::make_shared<MP4FileMemory>();
}
MP4FileIO::Writer MP4MuxerMemory::createWriter() {
return _memory_file->createWriter(MOV_FLAG_SEGMENT, true);
}
const string &MP4MuxerMemory::getInitSegment(){
if (_init_segment.empty()) {
initSegment();
saveSegment();
_init_segment = _memory_file->getAndClearMemory();
}
return _init_segment;
}
void MP4MuxerMemory::resetTracks(){
MP4MuxerInterface::resetTracks();
_memory_file = std::make_shared<MP4FileMemory>();
_init_segment.clear();
}
void MP4MuxerMemory::inputFrame(const Frame::Ptr &frame){
if (_init_segment.empty()) {
//尚未生成init segment
return;
}
bool key_frame = frame->keyFrame();
if (_ticker.elapsedTime() > 50 || key_frame) {
//遇到关键帧或者超过50ms则切片
_ticker.resetTime();
//flush切片
saveSegment();
//输出切片数据
onSegmentData(_memory_file->getAndClearMemory(), frame->dts(), _key_frame);
_key_frame = false;
}
if (key_frame) {
_key_frame = true;
}
MP4MuxerInterface::inputFrame(frame);
}
}//namespace mediakit
#endif//#ifdef ENABLE_MP4

View File

@@ -23,17 +23,16 @@
namespace mediakit{
class MP4Muxer : public MediaSinkInterface, public MP4File{
class MP4MuxerInterface : public MediaSinkInterface {
public:
typedef std::shared_ptr<MP4Muxer> Ptr;
MP4Muxer(const char *file);
~MP4Muxer() override;
MP4MuxerInterface() = default;
~MP4MuxerInterface() override = default;
/**
* 添加已经ready状态的track
*/
void addTrack(const Track::Ptr & track) override;
void addTrack(const Track::Ptr &track) override;
/**
* 输入帧
*/
@@ -42,30 +41,112 @@ public:
/**
* 重置所有track
*/
void resetTracks() override ;
void resetTracks() override;
/**
* 是否包含视频
*/
bool haveVideo() const;
/**
* 保存fmp4分片
*/
void saveSegment();
/**
* 创建新切片
*/
void initSegment();
protected:
virtual MP4FileIO::Writer createWriter() = 0;
private:
void stampSync();
private:
bool _started = false;
bool _have_video = false;
MP4FileIO::Writer _mov_writter;
struct track_info {
int track_id = -1;
Stamp stamp;
};
List<Frame::Ptr> _frameCached;
unordered_map<int, track_info> _codec_to_trackid;
};
class MP4Muxer : public MP4MuxerInterface{
public:
typedef std::shared_ptr<MP4Muxer> Ptr;
MP4Muxer();
~MP4Muxer() override;
/**
* 重置所有track
*/
void resetTracks() override;
/**
* 打开mp4
* @param file 文件完整路径
*/
void openMP4(const string &file);
/**
* 手动关闭文件(对象析构时会自动关闭)
*/
void closeMP4();
private:
void openMP4();
void stampSync();
protected:
MP4FileIO::Writer createWriter() override;
private:
struct track_info {
int track_id = -1;
Stamp stamp;
};
unordered_map<int, track_info> _codec_to_trackid;
List<Frame::Ptr> _frameCached;
bool _started = false;
bool _have_video = false;
MP4File::Writer _mov_writter;
string _file_name;
MP4FileDisk::Ptr _mp4_file;
};
class MP4MuxerMemory : public MP4MuxerInterface{
public:
MP4MuxerMemory();
~MP4MuxerMemory() override = default;
/**
* 重置所有track
*/
void resetTracks() override;
/**
* 输入帧
*/
void inputFrame(const Frame::Ptr &frame) override;
/**
* 获取fmp4 init segment
*/
const string &getInitSegment();
protected:
/**
* 输出fmp4切片回调函数
* @param string 切片内容
* @param stamp 切片末尾时间戳
* @param key_frame 是否有关键帧
*/
virtual void onSegmentData(const string &string, uint32_t stamp, bool key_frame) = 0;
protected:
MP4FileIO::Writer createWriter() override;
private:
bool _key_frame = false;
Ticker _ticker;
string _init_segment;
MP4FileMemory::Ptr _memory_file;
};
}//namespace mediakit
#endif//#ifdef ENABLE_MP4
#endif //ZLMEDIAKIT_MP4MUXER_H

View File

@@ -29,7 +29,8 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &
strFileName = File::absolutePath(strFileName,recordPath);
}
_demuxer = std::make_shared<MP4Demuxer>(strFileName.data());
_demuxer = std::make_shared<MP4Demuxer>();
_demuxer->openMP4(strFileName);
_mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _demuxer->getDurationMS() / 1000.0, true, true, false, false));
auto tracks = _demuxer->getTracks(false);
if(tracks.empty()){

View File

@@ -35,6 +35,7 @@ public:
* 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有)
*/
void startReadMP4();
private:
//MediaSourceEvent override
bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override;
@@ -45,15 +46,16 @@ private:
uint32_t getCurrentStamp();
void setCurrentStamp(uint32_t ui32Stamp);
bool seekTo(uint32_t ui32Stamp);
private:
recursive_mutex _mtx;
MultiMediaSourceMuxer::Ptr _mediaMuxer;
bool _have_video = false;
uint32_t _seek_to;
recursive_mutex _mtx;
Ticker _seek_ticker;
Timer::Ptr _timer;
EventPoller::Ptr _poller;
MP4Demuxer::Ptr _demuxer;
bool _have_video = false;
MultiMediaSourceMuxer::Ptr _mediaMuxer;
};
} /* namespace mediakit */

View File

@@ -25,10 +25,10 @@ MP4Recorder::MP4Recorder(const string& strPath,
const string &strStreamId) {
_strPath = strPath;
/////record 业务逻辑//////
_info.strAppName = strApp;
_info.strStreamId = strStreamId;
_info.strVhost = strVhost;
_info.strFolder = strPath;
_info.app = strApp;
_info.stream = strStreamId;
_info.vhost = strVhost;
_info.folder = strPath;
}
MP4Recorder::~MP4Recorder() {
closeFile();
@@ -42,26 +42,27 @@ void MP4Recorder::createFile() {
auto strFile = _strPath + strDate + "/" + strTime + ".mp4";
/////record 业务逻辑//////
_info.ui64StartedTime = ::time(NULL);
_info.strFileName = strTime + ".mp4";
_info.strFilePath = strFile;
_info.start_time = ::time(NULL);
_info.file_name = strTime + ".mp4";
_info.file_path = strFile;
GET_CONFIG(string,appName,Record::kAppName);
_info.strUrl = appName + "/"
+ _info.strAppName + "/"
+ _info.strStreamId + "/"
+ strDate + "/"
+ strTime + ".mp4";
_info.url = appName + "/"
+ _info.app + "/"
+ _info.stream + "/"
+ strDate + "/"
+ strTime + ".mp4";
try {
_muxer = std::make_shared<MP4Muxer>(strFileTmp.data());
for(auto &track :_tracks){
_muxer = std::make_shared<MP4Muxer>();
_muxer->openMP4(strFileTmp);
for (auto &track :_tracks) {
//添加track
_muxer->addTrack(track);
}
_strFileTmp = strFileTmp;
_strFile = strFile;
_createFileTicker.resetTime();
}catch(std::exception &ex) {
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
@@ -73,7 +74,7 @@ void MP4Recorder::asyncClose() {
auto info = _info;
WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
//获取文件录制时间放在关闭mp4之前是为了忽略关闭mp4执行时间
const_cast<MP4Info&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
const_cast<RecordInfo&>(info).time_len = ::time(NULL) - info.start_time;
//关闭mp4非常耗时所以要放在后台线程执行
muxer->closeMP4();
//临时文件名改成正式文件名防止mp4未完成时被访问
@@ -81,7 +82,7 @@ void MP4Recorder::asyncClose() {
//获取文件大小
struct stat fileData;
stat(strFile.data(), &fileData);
const_cast<MP4Info&>(info).ui64FileSize = fileData.st_size;
const_cast<RecordInfo&>(info).file_size = fileData.st_size;
/////record 业务逻辑//////
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
});

View File

@@ -20,24 +20,11 @@
#include "Util/TimeTicker.h"
#include "Common/MediaSink.h"
#include "MP4Muxer.h"
using namespace toolkit;
namespace mediakit {
class MP4Info {
public:
time_t ui64StartedTime; //GMT标准时间单位秒
time_t ui64TimeLen;//录像长度,单位秒
off_t ui64FileSize;//文件大小单位BYTE
string strFilePath;//文件路径
string strFileName;//文件名称
string strFolder;//文件夹路径
string strUrl;//播放路径
string strAppName;//应用名称
string strStreamId;//流ID
string strVhost;//vhost
};
#ifdef ENABLE_MP4
class MP4Recorder : public MediaSinkInterface{
public:
@@ -72,7 +59,7 @@ private:
string _strFile;
string _strFileTmp;
Ticker _createFileTicker;
MP4Info _info;
RecordInfo _info;
bool _haveVideo = false;
MP4Muxer::Ptr _muxer;
list<Track::Ptr> _tracks;

View File

@@ -79,16 +79,8 @@ std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const st
}
}
static MediaSource::Ptr getMediaSource(const string &vhost, const string &app, const string &stream_id){
auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id);
if(src){
return src;
}
return MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id);
}
bool Recorder::isRecording(type type, const string &vhost, const string &app, const string &stream_id){
auto src = getMediaSource(vhost, app, stream_id);
auto src = MediaSource::find(vhost, app, stream_id);
if(!src){
return false;
}
@@ -96,15 +88,16 @@ bool Recorder::isRecording(type type, const string &vhost, const string &app, co
}
bool Recorder::startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path){
auto src = getMediaSource(vhost, app, stream_id);
if(!src){
auto src = MediaSource::find(vhost, app, stream_id);
if (!src) {
WarnL << "未找到相关的MediaSource,startRecord失败:" << vhost << "/" << app << "/" << stream_id;
return false;
}
return src->setupRecord(type,true,customized_path);
return src->setupRecord(type, true, customized_path);
}
bool Recorder::stopRecord(type type, const string &vhost, const string &app, const string &stream_id){
auto src = getMediaSource(vhost, app, stream_id);
auto src = MediaSource::find(vhost, app, stream_id);
if(!src){
return false;
}

View File

@@ -16,6 +16,20 @@ using namespace std;
namespace mediakit {
class MediaSinkInterface;
class RecordInfo {
public:
time_t start_time; // GMT 标准时间,单位秒
float time_len; // 录像长度,单位秒
off_t file_size; // 文件大小,单位 BYTE
string file_path; // 文件路径
string file_name; // 文件名称
string folder; // 文件夹路径
string url; // 播放路径
string app; // 应用名称
string stream; // 流 ID
string vhost; // 虚拟主机
};
class Recorder{
public:
typedef enum {

View File

@@ -8,8 +8,8 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include "TsMuxer.h"
#if defined(ENABLE_HLS)
#include "TsMuxer.h"
#include "mpeg-ts-proto.h"
#include "mpeg-ts.h"
#include "Extension/H264.h"
@@ -25,20 +25,20 @@ TsMuxer::~TsMuxer() {
}
void TsMuxer::stampSync(){
if(_codec_to_trackid.size() < 2){
if (_codec_to_trackid.size() < 2) {
return;
}
Stamp *audio = nullptr, *video = nullptr;
for(auto &pr : _codec_to_trackid){
switch (getTrackType((CodecId) pr.first)){
for (auto &pr : _codec_to_trackid) {
switch (getTrackType((CodecId) pr.first)) {
case TrackAudio : audio = &pr.second.stamp; break;
case TrackVideo : video = &pr.second.stamp; break;
default : break;
}
}
if(audio && video){
if (audio && video) {
//音频时间戳同步于视频,因为音频时间戳被修改后不影响播放
audio->syncTo(*video);
}
@@ -73,6 +73,11 @@ void TsMuxer::addTrack(const Track::Ptr &track) {
break;
}
case CodecOpus: {
_codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_OPUS, nullptr, 0);
break;
}
default: WarnL << "mpeg-ts 不支持该编码格式,已忽略:" << track->getCodecName(); break;
}
@@ -82,88 +87,100 @@ void TsMuxer::addTrack(const Track::Ptr &track) {
void TsMuxer::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if(it == _codec_to_trackid.end()){
if (it == _codec_to_trackid.end()) {
return;
}
//mp4文件时间戳需要从0开始
auto &track_info = it->second;
int64_t dts_out, pts_out;
_is_idr_fast_packet = !_have_video;
switch (frame->getCodecId()){
case CodecH264: {
int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
if(type == H264Frame::NAL_SEI){
int type = H264_TYPE(*((uint8_t *) frame->data() + frame->prefixSize()));
if (type == H264Frame::NAL_SEI) {
break;
}
}
case CodecH265: {
//这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理
if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
Frame::Ptr back = _frameCached.back();
Buffer::Ptr merged_frame = back;
if(_frameCached.size() != 1){
if (_frameCached.size() != 1) {
string merged;
_frameCached.for_each([&](const Frame::Ptr &frame){
if(frame->prefixSize()){
merged.append(frame->data(),frame->size());
} else{
merged.append("\x00\x00\x00\x01",4);
merged.append(frame->data(),frame->size());
_frameCached.for_each([&](const Frame::Ptr &frame) {
if (frame->prefixSize()) {
merged.append(frame->data(), frame->size());
} else {
merged.append("\x00\x00\x00\x01", 4);
merged.append(frame->data(), frame->size());
}
if(frame->keyFrame()){
if (frame->keyFrame()) {
_is_idr_fast_packet = true;
}
});
merged_frame = std::make_shared<BufferString>(std::move(merged));
}
track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out);
track_info.stamp.revise(back->dts(), back->pts(), dts_out, pts_out);
//取视频时间戳为TS的时间戳
_timestamp = dts_out;
mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, merged_frame->data(), merged_frame->size());
mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL,dts_out * 90LL, merged_frame->data(), merged_frame->size());
_frameCached.clear();
}
_frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
}
break;
}
case CodecAAC: {
if (frame->prefixSize() == 0) {
WarnL << "必须提供adts头才能mpeg-ts打包";
break;
}
}
default: {
track_info.stamp.revise(frame->dts(),frame->pts(),dts_out,pts_out);
_timestamp = dts_out;
track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
if(!_have_video){
//没有视频时才以音频时间戳为TS的时间戳
_timestamp = dts_out;
}
mpeg_ts_write(_context, track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, frame->data(), frame->size());
}
break;
}
}
}
void TsMuxer::resetTracks() {
_have_video = false;
//通知片段中断
onTs(nullptr, 0, 0, 0);
onTs(nullptr, 0, _timestamp, 0);
uninit();
init();
}
void TsMuxer::init() {
static mpeg_ts_func_t s_func= {
[](void* param, size_t bytes){
TsMuxer *muxer = (TsMuxer *)param;
static mpeg_ts_func_t s_func = {
[](void *param, size_t bytes) {
TsMuxer *muxer = (TsMuxer *) param;
assert(sizeof(TsMuxer::_tsbuf) >= bytes);
return (void *)muxer->_tsbuf;
return (void *) muxer->_tsbuf;
},
[](void* param, void* packet){
[](void *param, void *packet) {
//do nothing
},
[](void* param, const void* packet, size_t bytes){
TsMuxer *muxer = (TsMuxer *)param;
muxer->onTs(packet, bytes,muxer->_timestamp,muxer->_is_idr_fast_packet);
[](void *param, const void *packet, size_t bytes) {
TsMuxer *muxer = (TsMuxer *) param;
muxer->onTs(packet, bytes, muxer->_timestamp, muxer->_is_idr_fast_packet);
muxer->_is_idr_fast_packet = false;
}
};
if(_context == nullptr){
_context = mpeg_ts_create(&s_func,this);
if (_context == nullptr) {
_context = mpeg_ts_create(&s_func, this);
}
}
void TsMuxer::uninit() {
if(_context){
if (_context) {
mpeg_ts_destroy(_context);
_context = nullptr;
}
@@ -171,5 +188,4 @@ void TsMuxer::uninit() {
}
}//namespace mediakit
#endif// defined(ENABLE_HLS)

View File

@@ -11,6 +11,7 @@
#ifndef TSMUXER_H
#define TSMUXER_H
#if defined(ENABLE_HLS)
#include <unordered_map>
#include "Extension/Frame.h"
#include "Extension/Track.h"
@@ -72,4 +73,25 @@ private:
};
}//namespace mediakit
#else
#include "Common/MediaSink.h"
namespace mediakit {
class TsMuxer : public MediaSinkInterface {
public:
TsMuxer() {}
~TsMuxer() override {}
void addTrack(const Track::Ptr &track) override {}
void resetTracks() override {}
void inputFrame(const Frame::Ptr &frame) override {}
protected:
virtual void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) = 0;
};
}//namespace mediakit
#endif// defined(ENABLE_HLS)
#endif //TSMUXER_H

View File

@@ -73,10 +73,10 @@ void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &mediaSrc) {
bool is_have_audio = false,is_have_video = false;
mediaSrc->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
if(pkt->typeId == MSG_VIDEO){
if(pkt->type_id == MSG_VIDEO){
is_have_video = true;
}
if(pkt->typeId == MSG_AUDIO){
if(pkt->type_id == MSG_AUDIO){
is_have_audio = true;
}
});
@@ -133,16 +133,16 @@ public:
#pragma pack(pop)
#endif // defined(_WIN32)
void FlvMuxer::onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t ui32TimeStamp , bool flush) {
onWriteFlvTag(pkt->typeId,pkt,ui32TimeStamp, flush);
void FlvMuxer::onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t time_stamp , bool flush) {
onWriteFlvTag(pkt->type_id, pkt, time_stamp, flush);
}
void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, bool flush) {
void FlvMuxer::onWriteFlvTag(uint8_t type, const Buffer::Ptr &buffer, uint32_t time_stamp, bool flush) {
RtmpTagHeader header;
header.type = ui8Type;
header.type = type;
set_be24(header.data_size, buffer->size());
header.timestamp_ex = (uint8_t) ((ui32TimeStamp >> 24) & 0xff);
set_be24(header.timestamp,ui32TimeStamp & 0xFFFFFF);
header.timestamp_ex = (uint8_t) ((time_stamp >> 24) & 0xff);
set_be24(header.timestamp, time_stamp & 0xFFFFFF);
//tag header
onWrite(std::make_shared<BufferRaw>((char *)&header, sizeof(header)), false);
//tag data
@@ -154,7 +154,7 @@ void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_
void FlvMuxer::onWriteRtmp(const RtmpPacket::Ptr &pkt,bool flush) {
int64_t dts_out;
_stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out);
_stamp[pkt->type_id % 2].revise(pkt->time_stamp, 0, dts_out, dts_out);
onWriteFlvTag(pkt, dts_out,flush);
}

View File

@@ -25,21 +25,23 @@ public:
FlvMuxer();
virtual ~FlvMuxer();
void stop();
protected:
void start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media);
void start(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &media);
virtual void onWrite(const Buffer::Ptr &data, bool flush) = 0;
virtual void onDetach() = 0;
virtual std::shared_ptr<FlvMuxer> getSharedPtr() = 0;
private:
void onWriteFlvHeader(const RtmpMediaSource::Ptr &media);
void onWriteRtmp(const RtmpPacket::Ptr &pkt,bool flush);
void onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t ui32TimeStamp, bool flush);
void onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, bool flush);
void onWriteRtmp(const RtmpPacket::Ptr &pkt, bool flush);
void onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t time_stamp, bool flush);
void onWriteFlvTag(uint8_t type, const Buffer::Ptr &buffer, uint32_t time_stamp, bool flush);
private:
RtmpMediaSource::RingType::RingReader::Ptr _ring_reader;
//时间戳修整器
Stamp _stamp[2];
RtmpMediaSource::RingType::RingReader::Ptr _ring_reader;
};
class FlvRecorder : public FlvMuxer , public std::enable_shared_from_this<FlvRecorder>{
@@ -47,12 +49,14 @@ public:
typedef std::shared_ptr<FlvRecorder> Ptr;
FlvRecorder();
virtual ~FlvRecorder();
void startRecord(const EventPoller::Ptr &poller,const string &vhost,const string &app,const string &stream,const string &file_path);
void startRecord(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media,const string &file_path);
void startRecord(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &media, const string &file_path);
void startRecord(const EventPoller::Ptr &poller, const string &vhost, const string &app, const string &stream, const string &file_path);
private:
virtual void onWrite(const Buffer::Ptr &data, bool flush) override ;
virtual void onDetach() override;
virtual std::shared_ptr<FlvMuxer> getSharedPtr() override;
private:
std::shared_ptr<FILE> _file;
recursive_mutex _file_mtx;
@@ -60,5 +64,4 @@ private:
}//namespace mediakit
#endif //ZLMEDIAKIT_FLVMUXER_H

View File

@@ -58,6 +58,14 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track){
switch (track->getCodecId()){
case CodecG711A : flvAudioType = FLV_CODEC_G711A; break;
case CodecG711U : flvAudioType = FLV_CODEC_G711U; break;
case CodecOpus : {
flvAudioType = FLV_CODEC_OPUS;
//opus不通过flags获取音频相关信息
iSampleRate = 44100;
iSampleBit = 16;
iChannel = 2;
break;
}
case CodecAAC : {
flvAudioType = FLV_CODEC_AAC;
//aac不通过flags获取音频相关信息

View File

@@ -23,9 +23,6 @@
using namespace toolkit;
#define PORT 1935
#define DEFAULT_CHUNK_LEN 128
#if !defined(_WIN32)
#define PACKED __attribute__((packed))
#else
@@ -33,6 +30,7 @@ using namespace toolkit;
#endif //!defined(_WIN32)
#define DEFAULT_CHUNK_LEN 128
#define HANDSHAKE_PLAINTEXT 0x03
#define RANDOM_LEN (1536 - 8)
@@ -74,10 +72,12 @@ using namespace toolkit;
#define FLV_CODEC_AAC 10
#define FLV_CODEC_H264 7
//金山扩展: https://github.com/ksvc/FFmpeg/wiki
#define FLV_CODEC_H265 12
#define FLV_CODEC_G711A 7
#define FLV_CODEC_G711U 8
//参考学而思网校: https://github.com/notedit/rtmp/commit/6e314ac5b29611431f8fb5468596b05815743c10
#define FLV_CODEC_OPUS 13
namespace mediakit {
@@ -89,22 +89,24 @@ class RtmpHandshake {
public:
RtmpHandshake(uint32_t _time, uint8_t *_random = nullptr) {
_time = htonl(_time);
memcpy(timeStamp, &_time, 4);
memcpy(time_stamp, &_time, 4);
if (!_random) {
random_generate((char *) random, sizeof(random));
} else {
memcpy(random, _random, sizeof(random));
}
}
uint8_t timeStamp[4];
uint8_t time_stamp[4];
uint8_t zero[4] = {0};
uint8_t random[RANDOM_LEN];
void random_generate(char* bytes, int size) {
static char cdata[] = { 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72,
0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x40, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6d };
void random_generate(char *bytes, int size) {
static char cdata[] = {0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72,
0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x40, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6d};
for (int i = 0; i < size; i++) {
bytes[i] = cdata[rand() % (sizeof(cdata) - 1)];
}
@@ -114,10 +116,10 @@ public:
class RtmpHeader {
public:
uint8_t flags;
uint8_t timeStamp[3];
uint8_t bodySize[3];
uint8_t typeId;
uint8_t streamId[4]; /* Note, this is little-endian while others are BE */
uint8_t time_stamp[3];
uint8_t body_size[3];
uint8_t type_id;
uint8_t stream_index[4]; /* Note, this is little-endian while others are BE */
}PACKED;
#if defined(_WIN32)
@@ -127,22 +129,23 @@ public:
class RtmpPacket : public Buffer{
public:
typedef std::shared_ptr<RtmpPacket> Ptr;
uint8_t typeId;
uint32_t bodySize = 0;
uint32_t timeStamp = 0;
bool hasAbsStamp = false;
bool hasExtStamp = false;
uint32_t deltaStamp = 0;
uint32_t streamId;
uint32_t chunkId;
std::string strBuf;
uint8_t type_id;
uint32_t body_size = 0;
uint32_t time_stamp = 0;
bool is_abs_stamp = false;
uint32_t ts_field = 0;
uint32_t stream_index;
uint32_t chunk_id;
std::string buffer;
public:
char *data() const override{
return (char*)strBuf.data();
return (char*)buffer.data();
}
uint32_t size() const override {
return strBuf.size();
};
return buffer.size();
}
public:
RtmpPacket() = default;
RtmpPacket(const RtmpPacket &that) = delete;
@@ -150,59 +153,64 @@ public:
RtmpPacket &operator=(RtmpPacket &&that) = delete;
RtmpPacket(RtmpPacket &&that){
typeId = that.typeId;
bodySize = that.bodySize;
timeStamp = that.timeStamp;
hasAbsStamp = that.hasAbsStamp;
hasExtStamp = that.hasExtStamp;
deltaStamp = that.deltaStamp;
streamId = that.streamId;
chunkId = that.chunkId;
strBuf = std::move(that.strBuf);
type_id = that.type_id;
body_size = that.body_size;
time_stamp = that.time_stamp;
is_abs_stamp = that.is_abs_stamp;
ts_field = that.ts_field;
stream_index = that.stream_index;
chunk_id = that.chunk_id;
buffer = std::move(that.buffer);
}
bool isVideoKeyFrame() const {
return typeId == MSG_VIDEO && (uint8_t) strBuf[0] >> 4 == FLV_KEY_FRAME && (uint8_t) strBuf[1] == 1;
return type_id == MSG_VIDEO && (uint8_t) buffer[0] >> 4 == FLV_KEY_FRAME && (uint8_t) buffer[1] == 1;
}
bool isCfgFrame() const {
switch (typeId){
case MSG_VIDEO : return strBuf[1] == 0;
switch (type_id){
case MSG_VIDEO : return buffer[1] == 0;
case MSG_AUDIO : {
switch (getMediaType()){
case FLV_CODEC_AAC : return strBuf[1] == 0;
case FLV_CODEC_AAC : return buffer[1] == 0;
default : return false;
}
}
default : return false;
}
}
int getMediaType() const {
switch (typeId) {
case MSG_VIDEO : return (uint8_t) strBuf[0] & 0x0F;
case MSG_AUDIO : return (uint8_t) strBuf[0] >> 4;
switch (type_id) {
case MSG_VIDEO : return (uint8_t) buffer[0] & 0x0F;
case MSG_AUDIO : return (uint8_t) buffer[0] >> 4;
default : return 0;
}
}
int getAudioSampleRate() const {
if (typeId != MSG_AUDIO) {
if (type_id != MSG_AUDIO) {
return 0;
}
int flvSampleRate = ((uint8_t) strBuf[0] & 0x0C) >> 2;
int flvSampleRate = ((uint8_t) buffer[0] & 0x0C) >> 2;
const static int sampleRate[] = { 5512, 11025, 22050, 44100 };
return sampleRate[flvSampleRate];
}
int getAudioSampleBit() const {
if (typeId != MSG_AUDIO) {
if (type_id != MSG_AUDIO) {
return 0;
}
int flvSampleBit = ((uint8_t) strBuf[0] & 0x02) >> 1;
int flvSampleBit = ((uint8_t) buffer[0] & 0x02) >> 1;
const static int sampleBit[] = { 8, 16 };
return sampleBit[flvSampleBit];
}
int getAudioChannel() const {
if (typeId != MSG_AUDIO) {
if (type_id != MSG_AUDIO) {
return 0;
}
int flvStereoOrMono = (uint8_t) strBuf[0] & 0x01;
int flvStereoOrMono = (uint8_t) buffer[0] & 0x01;
const static int channel[] = { 1, 2 };
return channel[flvStereoOrMono];
}

View File

@@ -23,14 +23,14 @@ public:
typedef std::shared_ptr<RtmpRing> Ptr;
typedef RingBuffer<RtmpPacket::Ptr> RingType;
RtmpRing(){}
virtual ~RtmpRing(){}
RtmpRing() {}
virtual ~RtmpRing() {}
/**
* 获取rtmp环形缓存
* @return
*/
virtual RingType::Ptr getRtmpRing() const{
virtual RingType::Ptr getRtmpRing() const {
return _rtmpRing;
}
@@ -38,27 +38,24 @@ public:
* 设置rtmp环形缓存
* @param ring
*/
virtual void setRtmpRing(const RingType::Ptr &ring){
virtual void setRtmpRing(const RingType::Ptr &ring) {
_rtmpRing = ring;
}
/**
* 输入rtmp包
* @param rtmp rtmp包
* @param key_pos 是否为关键帧
* @return 是否为关键帧
*/
virtual bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos){
if(_rtmpRing){
_rtmpRing->write(rtmp,key_pos);
virtual void inputRtmp(const RtmpPacket::Ptr &rtmp) {
if (_rtmpRing) {
_rtmpRing->write(rtmp, rtmp->isVideoKeyFrame());
}
return key_pos;
}
protected:
RingType::Ptr _rtmpRing;
};
class RtmpCodec : public RtmpRing, public FrameDispatcher , public CodecInfo{
public:
typedef std::shared_ptr<RtmpCodec> Ptr;
@@ -69,5 +66,4 @@ public:
}//namespace mediakit
#endif //ZLMEDIAKIT_RTMPCODEC_H

View File

@@ -65,34 +65,32 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val){
return ret;
}
bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
switch (pkt->typeId) {
void RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
switch (pkt->type_id) {
case MSG_VIDEO: {
if(!_tryedGetVideoTrack){
_tryedGetVideoTrack = true;
if (!_try_get_video_track) {
_try_get_video_track = true;
auto codec = AMFValue(pkt->getMediaType());
makeVideoTrack(codec);
}
if(_videoRtmpDecoder){
return _videoRtmpDecoder->inputRtmp(pkt, true);
if (_video_rtmp_decoder) {
_video_rtmp_decoder->inputRtmp(pkt);
}
return false;
break;
}
case MSG_AUDIO: {
if(!_tryedGetAudioTrack) {
_tryedGetAudioTrack = true;
if (!_try_get_audio_track) {
_try_get_audio_track = true;
auto codec = AMFValue(pkt->getMediaType());
makeAudioTrack(codec, pkt->getAudioSampleRate(), pkt->getAudioChannel(), pkt->getAudioSampleBit());
}
if(_audioRtmpDecoder){
_audioRtmpDecoder->inputRtmp(pkt, false);
return false;
if (_audio_rtmp_decoder) {
_audio_rtmp_decoder->inputRtmp(pkt);
}
return false;
break;
}
default:
return false;
default : break;
}
}
@@ -101,12 +99,12 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) {
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getVideoTrackByAmf(videoCodec));
if (_videoTrack) {
//生成rtmpCodec对象以便解码rtmp
_videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack, false);
if (_videoRtmpDecoder) {
_video_rtmp_decoder = Factory::getRtmpCodecByTrack(_videoTrack, false);
if (_video_rtmp_decoder) {
//设置rtmp解码器代理生成的frame写入该Track
_videoRtmpDecoder->addDelegate(_videoTrack);
_video_rtmp_decoder->addDelegate(_videoTrack);
onAddTrack(_videoTrack);
_tryedGetVideoTrack = true;
_try_get_video_track = true;
} else {
//找不到相应的rtmp解码器该track无效
_videoTrack.reset();
@@ -119,12 +117,12 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec,int sample_rate, int
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit));
if (_audioTrack) {
//生成rtmpCodec对象以便解码rtmp
_audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack, false);
if (_audioRtmpDecoder) {
_audio_rtmp_decoder = Factory::getRtmpCodecByTrack(_audioTrack, false);
if (_audio_rtmp_decoder) {
//设置rtmp解码器代理生成的frame写入该Track
_audioRtmpDecoder->addDelegate(_audioTrack);
_audio_rtmp_decoder->addDelegate(_audioTrack);
onAddTrack(_audioTrack);
_tryedGetAudioTrack = true;
_try_get_audio_track = true;
} else {
//找不到相应的rtmp解码器该track无效
_audioTrack.reset();

View File

@@ -28,24 +28,25 @@ public:
typedef std::shared_ptr<RtmpDemuxer> Ptr;
RtmpDemuxer() = default;
virtual ~RtmpDemuxer() = default;
~RtmpDemuxer() override = default;
bool loadMetaData(const AMFValue &metadata);
/**
* 开始解复用
* @param pkt rtmp包
* @return true 代表是i帧
*/
bool inputRtmp(const RtmpPacket::Ptr &pkt);
void inputRtmp(const RtmpPacket::Ptr &pkt);
private:
void makeVideoTrack(const AMFValue &val);
void makeAudioTrack(const AMFValue &val, int sample_rate, int channels, int sample_bit);
private:
bool _tryedGetVideoTrack = false;
bool _tryedGetAudioTrack = false;
RtmpCodec::Ptr _audioRtmpDecoder;
RtmpCodec::Ptr _videoRtmpDecoder;
bool _try_get_video_track = false;
bool _try_get_audio_track = false;
RtmpCodec::Ptr _audio_rtmp_decoder;
RtmpCodec::Ptr _video_rtmp_decoder;
};
} /* namespace mediakit */

View File

@@ -60,7 +60,7 @@ public:
MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {
}
virtual ~RtmpMediaSource() {}
~RtmpMediaSource() override{}
/**
* 获取媒体源的环形缓冲
@@ -100,9 +100,8 @@ public:
* 设置metadata
*/
virtual void setMetaData(const AMFValue &metadata) {
lock_guard<recursive_mutex> lock(_mtx);
_metadata = metadata;
if(_ring){
if (_ring) {
regist();
}
}
@@ -118,25 +117,24 @@ public:
/**
* 输入rtmp包
* @param pkt rtmp包
* @param key 是否为关键帧
*/
void onWrite(const RtmpPacket::Ptr &pkt, bool key = true) override {
lock_guard<recursive_mutex> lock(_mtx);
if(pkt->typeId == MSG_VIDEO){
//有视频那么启用GOP缓存
_have_video = true;
void onWrite(const RtmpPacket::Ptr &pkt, bool = true) override {
//保存当前时间戳
switch (pkt->type_id) {
case MSG_VIDEO : _track_stamps[TrackVideo] = pkt->time_stamp, _have_video = true; break;
case MSG_AUDIO : _track_stamps[TrackAudio] = pkt->time_stamp; break;
default : break;
}
if (pkt->isCfgFrame()) {
_config_frame_map[pkt->typeId] = pkt;
lock_guard<recursive_mutex> lock(_mtx);
_config_frame_map[pkt->type_id] = pkt;
return;
}
//保存当前时间戳
_track_stamps_map[pkt->typeId] = pkt->timeStamp;
if (!_ring) {
weak_ptr<RtmpMediaSource> weakSelf = dynamic_pointer_cast<RtmpMediaSource>(shared_from_this());
auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) {
auto lam = [weakSelf](int size) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
@@ -144,9 +142,8 @@ public:
strongSelf->onReaderChanged(size);
};
//rtmp包缓存最大允许512个如果是纯视频(25fps)大概为20秒数据
//但是这个是GOP缓存的上限值真实的GOP缓存大小等于两个I帧之间的包数的两倍
//而且每次遇到I帧则会清空GOP缓存所以真实的GOP缓存远小于此值
//GOP默认缓冲512组RTMP包每组RTMP包时间戳相同(如果开启合并写了那么每组为合并写时间内的RTMP包),
//每次遇到关键帧第一个RTMP包则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开)
_ring = std::make_shared<RingType>(_ring_size,std::move(lam));
onReaderChanged(0);
@@ -154,26 +151,35 @@ public:
regist();
}
}
PacketCache<RtmpPacket>::inputPacket(pkt->typeId == MSG_VIDEO, pkt, key);
PacketCache<RtmpPacket>::inputPacket(pkt->type_id == MSG_VIDEO, pkt, pkt->isVideoKeyFrame());
}
/**
* 获取当前时间戳
*/
uint32_t getTimeStamp(TrackType trackType) override {
lock_guard<recursive_mutex> lock(_mtx);
switch (trackType) {
case TrackVideo:
return _track_stamps_map[MSG_VIDEO];
case TrackAudio:
return _track_stamps_map[MSG_AUDIO];
default:
return MAX(_track_stamps_map[MSG_VIDEO], _track_stamps_map[MSG_AUDIO]);
assert(trackType >= TrackInvalid && trackType < TrackMax);
if (trackType != TrackInvalid) {
//获取某track的时间戳
return _track_stamps[trackType];
}
//获取所有track的最小时间戳
uint32_t ret = UINT32_MAX;
for (auto &stamp : _track_stamps) {
if (stamp > 0 && stamp < ret) {
ret = stamp;
}
}
return ret;
}
void clearCache() override{
PacketCache<RtmpPacket>::clearCache();
_ring->clearCache();
}
private:
/**
* 批量flush rtmp包时触发该函数
* @param rtmp_list rtmp包列表
@@ -184,22 +190,14 @@ private:
_ring->write(rtmp_list, _have_video ? key_pos : true);
}
/**
* 每次增减消费者都会触发该函数
*/
void onReaderChanged(int size) {
if (size == 0) {
onNoneReader();
}
}
private:
int _ring_size;
bool _have_video = false;
mutable recursive_mutex _mtx;
int _ring_size;
uint32_t _track_stamps[TrackMax] = {0};
AMFValue _metadata;
RingType::Ptr _ring;
unordered_map<int, uint32_t> _track_stamps_map;
mutable recursive_mutex _mtx;
unordered_map<int, RtmpPacket::Ptr> _config_frame_map;
};

View File

@@ -60,27 +60,12 @@ public:
/**
* 输入rtmp并解析
*/
void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override {
if(_all_track_ready && !_muxer->isEnabled()){
//获取到所有Track后并且未开启转协议,那么需要解复用rtmp
key_pos = pkt->isVideoKeyFrame();
}else{
//需要解复用rtmp
key_pos = _demuxer->inputRtmp(pkt);
}
RtmpMediaSource::onWrite(pkt,key_pos);
}
/**
* 设置监听器
* @param listener
*/
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
RtmpMediaSource::setListener(listener);
if(_muxer){
_muxer->setMediaListener(listener);
void onWrite(const RtmpPacket::Ptr &pkt, bool = true) override {
if (!_all_track_ready || _muxer->isEnabled()) {
//获取到所有Track后或者开启转协议那么需要解复用rtmp
_demuxer->inputRtmp(pkt);
}
RtmpMediaSource::onWrite(pkt);
}
/**
@@ -90,44 +75,19 @@ public:
return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0);
}
/**
* 设置录制状态
* @param type 录制类型
* @param start 开始或停止
* @param custom_path 开启录制时,指定自定义路径
* @return 是否设置成功
*/
bool setupRecord(Recorder::type type, bool start, const string &custom_path) override{
if(_muxer){
return _muxer->setupRecord(*this,type, start, custom_path);
}
return RtmpMediaSource::setupRecord(type, start, custom_path);
}
/**
* 获取录制状态
* @param type 录制类型
* @return 录制状态
*/
bool isRecording(Recorder::type type) override{
if(_muxer){
return _muxer->isRecording(*this,type);
}
return RtmpMediaSource::isRecording(type);
}
/**
* 设置协议转换
* @param enableRtsp 是否转换成rtsp
* @param enableHls 是否转换成hls
* @param enableMP4 是否mp4录制
*/
void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) {
void setProtocolTranslation(bool enableHls, bool enableMP4) {
//不重复生成rtmp
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4);
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(), getApp(), getId(), _demuxer->getDuration(), true, false, enableHls, enableMP4);
_muxer->setMediaListener(getListener());
_muxer->setTrackListener(this);
_muxer->setTrackListener(static_pointer_cast<RtmpMediaSourceImp>(shared_from_this()));
//让_muxer对象拦截一部分事件(比如说录像相关事件)
setListener(_muxer);
for(auto &track : _demuxer->getTracks(false)){
_muxer->addTrack(track);
track->addDelegate(_muxer);
@@ -148,12 +108,11 @@ public:
* _muxer触发的所有Track就绪的事件
*/
void onAllTrackReady() override{
setTrackSource(_muxer);
_all_track_ready = true;
if (_recreate_metadata) {
//更新metadata
for (auto &track : _muxer->getTracks()) {
for (auto &track : _muxer->getTracks(*this)) {
Metadata::addTrack(_metadata, track);
}
RtmpMediaSource::updateMetaData(_metadata);
@@ -161,11 +120,12 @@ public:
}
private:
RtmpDemuxer::Ptr _demuxer;
MultiMediaSourceMuxer::Ptr _muxer;
AMFValue _metadata;
bool _all_track_ready = false;
bool _recreate_metadata = false;
AMFValue _metadata;
RtmpDemuxer::Ptr _demuxer;
MultiMediaSourceMuxer::Ptr _muxer;
};
} /* namespace mediakit */

View File

@@ -16,7 +16,8 @@
namespace mediakit {
class RtmpMediaSourceMuxer : public RtmpMuxer {
class RtmpMediaSourceMuxer : public RtmpMuxer, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<RtmpMediaSourceMuxer> {
public:
typedef std::shared_ptr<RtmpMediaSourceMuxer> Ptr;
@@ -24,34 +25,57 @@ public:
const string &strApp,
const string &strId,
const TitleMeta::Ptr &title = nullptr) : RtmpMuxer(title){
_mediaSouce = std::make_shared<RtmpMediaSource>(vhost,strApp,strId);
getRtmpRing()->setDelegate(_mediaSouce);
_media_src = std::make_shared<RtmpMediaSource>(vhost, strApp, strId);
getRtmpRing()->setDelegate(_media_src);
}
virtual ~RtmpMediaSourceMuxer(){}
~RtmpMediaSourceMuxer() override{}
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
_mediaSouce->setListener(listener);
_listener = listener;
_media_src->setListener(shared_from_this());
}
void setTimeStamp(uint32_t stamp){
_mediaSouce->setTimeStamp(stamp);
_media_src->setTimeStamp(stamp);
}
int readerCount() const{
return _mediaSouce->readerCount();
return _media_src->readerCount();
}
void onAllTrackReady(){
makeConfigPacket();
_mediaSouce->setMetaData(getMetadata());
_media_src->setMetaData(getMetadata());
}
// 设置TrackSource
void setTrackSource(const std::weak_ptr<TrackSource> &track_src){
_mediaSouce->setTrackSource(track_src);
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) {
RtmpMuxer::inputFrame(frame);
}
}
bool isEnabled() {
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return _clear_cache ? true : _enabled;
}
private:
RtmpMediaSource::Ptr _mediaSouce;
bool _enabled = true;
bool _clear_cache = false;
RtmpMediaSource::Ptr _media_src;
};

View File

@@ -19,7 +19,7 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) {
}else{
_metadata = title->getMetadata();
}
_rtmpRing = std::make_shared<RtmpRing::RingType>();
_rtmp_ring = std::make_shared<RtmpRing::RingType>();
}
void RtmpMuxer::addTrack(const Track::Ptr &track) {
@@ -31,7 +31,7 @@ void RtmpMuxer::addTrack(const Track::Ptr &track) {
}
//设置rtmp输出环形缓存
encoder->setRtmpRing(_rtmpRing);
encoder->setRtmpRing(_rtmp_ring);
//添加metadata
Metadata::addTrack(_metadata,track);
@@ -57,7 +57,7 @@ const AMFValue &RtmpMuxer::getMetadata() const {
}
RtmpRing::RingType::Ptr RtmpMuxer::getRtmpRing() const {
return _rtmpRing;
return _rtmp_ring;
}
void RtmpMuxer::resetTracks() {

View File

@@ -61,7 +61,7 @@ public:
*/
void makeConfigPacket();
private:
RtmpRing::RingType::Ptr _rtmpRing;
RtmpRing::RingType::Ptr _rtmp_ring;
AMFValue _metadata;
RtmpCodec::Ptr _encoder[TrackMax];
};

View File

@@ -18,8 +18,7 @@ using namespace mediakit::Client;
namespace mediakit {
RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) {
}
RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) {}
RtmpPlayer::~RtmpPlayer() {
DebugL << endl;
@@ -29,97 +28,82 @@ void RtmpPlayer::teardown() {
if (alive()) {
shutdown(SockException(Err_shutdown,"teardown"));
}
_strApp.clear();
_strStream.clear();
_strTcUrl.clear();
_pBeatTimer.reset();
_pPlayTimer.reset();
_pMediaTimer.reset();
_iSeekTo = 0;
_app.clear();
_stream_id.clear();
_tc_url.clear();
_beat_timer.reset();
_play_timer.reset();
_rtmp_recv_timer.reset();
_seek_ms = 0;
RtmpProtocol::reset();
CLEAR_ARR(_aiFistStamp);
CLEAR_ARR(_aiNowStamp);
CLEAR_ARR(_fist_stamp);
CLEAR_ARR(_now_stamp);
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
_mapOnResultCB.clear();
lock_guard<recursive_mutex> lck2(_mtxOnStatusCB);
_dqOnStatusCB.clear();
lock_guard<recursive_mutex> lck(_mtx_on_result);
_map_on_result.clear();
lock_guard<recursive_mutex> lck2(_mtx_on_status);
_deque_on_status.clear();
}
void RtmpPlayer::play(const string &strUrl) {
teardown();
string strHost = FindField(strUrl.data(), "://", "/");
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
_strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL);
_strTcUrl = string("rtmp://") + strHost + "/" + _strApp;
string host_url = FindField(strUrl.data(), "://", "/");
_app = FindField(strUrl.data(), (host_url + "/").data(), "/");
_stream_id = FindField(strUrl.data(), (host_url + "/" + _app + "/").data(), NULL);
_tc_url = string("rtmp://") + host_url + "/" + _app;
if (!_strApp.size() || !_strStream.size()) {
onPlayResult_l(SockException(Err_other,"rtmp url非法"),false);
if (!_app.size() || !_stream_id.size()) {
onPlayResult_l(SockException(Err_other, "rtmp url非法"), false);
return;
}
DebugL << strHost << " " << _strApp << " " << _strStream;
DebugL << host_url << " " << _app << " " << _stream_id;
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
auto iPort = atoi(FindField(host_url.data(), ":", NULL).data());
if (iPort <= 0) {
//rtmp 默认端口1935
iPort = 1935;
} else {
//服务器域名
strHost = FindField(strHost.data(), NULL, ":");
host_url = FindField(host_url.data(), NULL, ":");
}
if(!(*this)[kNetAdapter].empty()){
if (!(*this)[kNetAdapter].empty()) {
setNetAdapter((*this)[kNetAdapter]);
}
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
_pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() {
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
weak_ptr<RtmpPlayer> weak_self = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
float play_timeout_sec = (*this)[kTimeoutMS].as<int>() / 1000.0;
_play_timer.reset(new Timer(play_timeout_sec, [weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return false;
}
strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtmp timeout"),false);
strong_self->onPlayResult_l(SockException(Err_timeout, "play rtmp timeout"), false);
return false;
},getPoller()));
}, getPoller()));
_metadata_got = false;
startConnect(strHost, iPort , playTimeOutSec);
startConnect(host_url, iPort, play_timeout_sec);
}
void RtmpPlayer::onErr(const SockException &ex){
//定时器_pPlayTimer为空后表明握手结束了
onPlayResult_l(ex, !_pPlayTimer);
onPlayResult_l(ex, !_play_timer);
}
void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) {
WarnL << ex.getErrCode() << " " << ex.what();
if(!ex){
//播放成功恢复rtmp接收超时定时器
_mediaTicker.resetTime();
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
int timeoutMS = (*this)[kMediaTimeoutMS].as<int>();
//创建rtmp数据接收超时检测定时器
_pMediaTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() {
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
return false;
}
if(strongSelf->_mediaTicker.elapsedTime()> timeoutMS) {
//接收rtmp媒体数据超时
strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtmp timeout"),true);
return false;
}
return true;
},getPoller()));
void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) {
if (ex.getErrCode() == Err_shutdown) {
//主动shutdown的不触发回调
return;
}
if (!handshakeCompleted) {
WarnL << ex.getErrCode() << " " << ex.what();
if (!handshake_done) {
//开始播放阶段
_pPlayTimer.reset();
onPlayResult(ex);
_play_timer.reset();
//是否为性能测试模式
_benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
onPlayResult(ex);
} else if (ex) {
//播放成功后异常断开回调
onShutdown(ex);
@@ -128,36 +112,58 @@ void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete
onResume();
}
if(ex){
if (!ex) {
//播放成功恢复rtmp接收超时定时器
_rtmp_recv_ticker.resetTime();
int timeout_ms = (*this)[kMediaTimeoutMS].as<int>();
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
auto lam = [weakSelf, timeout_ms]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return false;
}
if (strongSelf->_rtmp_recv_ticker.elapsedTime() > timeout_ms) {
//接收rtmp媒体数据超时
SockException ex(Err_timeout, "receive rtmp timeout");
strongSelf->onPlayResult_l(ex, true);
return false;
}
return true;
};
//创建rtmp数据接收超时检测定时器
_rtmp_recv_timer = std::make_shared<Timer>(timeout_ms / 2000.0, lam, getPoller());
} else {
teardown();
}
}
void RtmpPlayer::onConnect(const SockException &err){
if(err.getErrCode() != Err_success) {
if (err.getErrCode() != Err_success) {
onPlayResult_l(err, false);
return;
}
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
startClientSession([weakSelf](){
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
startClientSession([weakSelf]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->send_connect();
});
}
void RtmpPlayer::onRecv(const Buffer::Ptr &pBuf){
void RtmpPlayer::onRecv(const Buffer::Ptr &buf){
try {
if(_benchmark_mode && !_pPlayTimer){
if (_benchmark_mode && !_play_timer) {
//在性能测试模式下如果rtmp握手完毕后不再解析rtmp包
_mediaTicker.resetTime();
_rtmp_recv_ticker.resetTime();
return;
}
onParseRtmp(pBuf->data(), pBuf->size());
onParseRtmp(buf->data(), buf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
//定时器_pPlayTimer为空后表明握手结束了
onPlayResult_l(ex, !_pPlayTimer);
onPlayResult_l(ex, !_play_timer);
}
}
@@ -167,8 +173,8 @@ void RtmpPlayer::pause(bool bPause) {
inline void RtmpPlayer::send_connect() {
AMFValue obj(AMF_OBJECT);
obj.set("app", _strApp);
obj.set("tcUrl", _strTcUrl);
obj.set("app", _app);
obj.set("tcUrl", _tc_url);
//未使用代理
obj.set("fpad", false);
//参考librtmp,什么作用?
@@ -176,18 +182,18 @@ inline void RtmpPlayer::send_connect() {
//SUPPORT_VID_CLIENT_SEEK 支持seek
obj.set("videoFunction", 1);
//只支持aac
obj.set("audioCodecs", (double)(0x0400));
obj.set("audioCodecs", (double) (0x0400));
//只支持H264
obj.set("videoCodecs", (double)(0x0080));
obj.set("videoCodecs", (double) (0x0080));
sendInvoke("connect", obj);
addOnResultCB([this](AMFDecoder &dec){
addOnResultCB([this](AMFDecoder &dec) {
//TraceL << "connect result";
dec.load<AMFValue>();
auto val = dec.load<AMFValue>();
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status"){
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
if (level != "status") {
throw std::runtime_error(StrPrinter << "connect 失败:" << level << " " << code << endl);
}
send_createStream();
});
@@ -196,102 +202,103 @@ inline void RtmpPlayer::send_connect() {
inline void RtmpPlayer::send_createStream() {
AMFValue obj(AMF_NULL);
sendInvoke("createStream", obj);
addOnResultCB([this](AMFDecoder &dec){
addOnResultCB([this](AMFDecoder &dec) {
//TraceL << "createStream result";
dec.load<AMFValue>();
_ui32StreamId = dec.load<int>();
_stream_index = dec.load<int>();
send_play();
});
}
inline void RtmpPlayer::send_play() {
AMFEncoder enc;
enc << "play" << ++_iReqID << nullptr << _strStream << (double)_ui32StreamId;
enc << "play" << ++_send_req_id << nullptr << _stream_id << (double) _stream_index;
sendRequest(MSG_CMD, enc.data());
auto fun = [this](AMFValue &val){
auto fun = [this](AMFValue &val) {
//TraceL << "play onStatus";
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status"){
throw std::runtime_error(StrPrinter <<"play 失败:" << level << " " << code << endl);
if (level != "status") {
throw std::runtime_error(StrPrinter << "play 失败:" << level << " " << code << endl);
}
};
addOnStatusCB(fun);
addOnStatusCB(fun);
}
inline void RtmpPlayer::send_pause(bool bPause) {
inline void RtmpPlayer::send_pause(bool pause) {
AMFEncoder enc;
enc << "pause" << ++_iReqID << nullptr << bPause;
enc << "pause" << ++_send_req_id << nullptr << pause;
sendRequest(MSG_CMD, enc.data());
auto fun = [this,bPause](AMFValue &val){
auto fun = [this, pause](AMFValue &val) {
//TraceL << "pause onStatus";
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status") {
if(!bPause){
throw std::runtime_error(StrPrinter <<"pause 恢复播放失败:" << level << " " << code << endl);
if (level != "status") {
if (!pause) {
throw std::runtime_error(StrPrinter << "pause 恢复播放失败:" << level << " " << code << endl);
}
}else{
_bPaused = bPause;
if(!bPause){
} else {
_paused = pause;
if (!pause) {
onPlayResult_l(SockException(Err_success, "resum rtmp success"), true);
}else{
} else {
//暂停播放
_pMediaTimer.reset();
_rtmp_recv_timer.reset();
}
}
};
addOnStatusCB(fun);
_pBeatTimer.reset();
if(bPause){
_beat_timer.reset();
if (pause) {
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
_pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0,[weakSelf](){
_beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0, [weakSelf]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf){
if (!strongSelf) {
return false;
}
uint32_t timeStamp = ::time(NULL);
strongSelf->sendUserControl(CONTROL_PING_REQUEST, timeStamp);
return true;
},getPoller()));
}, getPoller()));
}
}
void RtmpPlayer::onCmd_result(AMFDecoder &dec){
auto iReqId = dec.load<int>();
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
auto it = _mapOnResultCB.find(iReqId);
if(it != _mapOnResultCB.end()){
auto req_id = dec.load<int>();
lock_guard<recursive_mutex> lck(_mtx_on_result);
auto it = _map_on_result.find(req_id);
if (it != _map_on_result.end()) {
it->second(dec);
_mapOnResultCB.erase(it);
}else{
_map_on_result.erase(it);
} else {
WarnL << "unhandled _result";
}
}
void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) {
AMFValue val;
while(true){
while (true) {
val = dec.load<AMFValue>();
if(val.type() == AMF_OBJECT){
if (val.type() == AMF_OBJECT) {
break;
}
}
if(val.type() != AMF_OBJECT){
if (val.type() != AMF_OBJECT) {
throw std::runtime_error("onStatus:the result object was not found");
}
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
if(_dqOnStatusCB.size()){
_dqOnStatusCB.front()(val);
_dqOnStatusCB.pop_front();
}else{
lock_guard<recursive_mutex> lck(_mtx_on_status);
if (_deque_on_status.size()) {
_deque_on_status.front()(val);
_deque_on_status.pop_front();
} else {
auto level = val["level"];
auto code = val["code"].as_string();
if(level.type() == AMF_STRING){
if(level.as_string() != "status"){
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
if (level.type() == AMF_STRING) {
if (level.as_string() != "status") {
throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl);
}
}
//WarnL << "unhandled onStatus:" << code;
@@ -301,112 +308,111 @@ void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) {
void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) {
//TraceL;
auto val = dec.load<AMFValue>();
if(!onCheckMeta(val)){
if (!onCheckMeta(val)) {
throw std::runtime_error("onCheckMeta failed");
}
_metadata_got = true;
}
void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) {
//TraceL << ui32StreamId;
onPlayResult_l(SockException(Err_other,"rtmp stream dry"), true);
void RtmpPlayer::onStreamDry(uint32_t stream_index) {
//TraceL << stream_index;
onPlayResult_l(SockException(Err_other, "rtmp stream dry"), true);
}
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) {
_mediaTicker.resetTime();
if(!_pPlayTimer){
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &chunk_data) {
_rtmp_recv_ticker.resetTime();
if (!_play_timer) {
//已经触发了onPlayResult事件直接触发onMediaData事件
onMediaData(packet);
onMediaData(chunk_data);
return;
}
if(packet->isCfgFrame()){
if (chunk_data->isCfgFrame()) {
//输入配置帧以便初始化完成各个track
onMediaData(packet);
}else{
onMediaData(chunk_data);
} else {
//先触发onPlayResult事件这个时候解码器才能初始化完毕
onPlayResult_l(SockException(Err_success,"play rtmp success"), false);
onPlayResult_l(SockException(Err_success, "play rtmp success"), false);
//触发onPlayResult事件后再把帧数据输入到解码器
onMediaData(packet);
onMediaData(chunk_data);
}
}
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunk_data) {
typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec);
static unordered_map<string, rtmp_func_ptr> s_func_map;
static onceToken token([]() {
s_func_map.emplace("_error",&RtmpPlayer::onCmd_result);
s_func_map.emplace("_result",&RtmpPlayer::onCmd_result);
s_func_map.emplace("onStatus",&RtmpPlayer::onCmd_onStatus);
s_func_map.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData);
}, []() {});
s_func_map.emplace("_error", &RtmpPlayer::onCmd_result);
s_func_map.emplace("_result", &RtmpPlayer::onCmd_result);
s_func_map.emplace("onStatus", &RtmpPlayer::onCmd_onStatus);
s_func_map.emplace("onMetaData", &RtmpPlayer::onCmd_onMetaData);
});
switch (chunkData.typeId) {
switch (chunk_data.type_id) {
case MSG_CMD:
case MSG_CMD3:
case MSG_DATA:
case MSG_DATA3: {
AMFDecoder dec(chunkData.strBuf, 0);
AMFDecoder dec(chunk_data.buffer, 0);
std::string type = dec.load<std::string>();
auto it = s_func_map.find(type);
if(it != s_func_map.end()){
if (it != s_func_map.end()) {
auto fun = it->second;
(this->*fun)(dec);
}else{
} else {
WarnL << "can not support cmd:" << type;
}
}
break;
}
case MSG_AUDIO:
case MSG_VIDEO: {
auto idx = chunkData.typeId%2;
if (_aNowStampTicker[idx].elapsedTime() > 500) {
auto idx = chunk_data.type_id % 2;
if (_now_stamp_ticker[idx].elapsedTime() > 500) {
//计算播放进度时间轴用
_aiNowStamp[idx] = chunkData.timeStamp;
_now_stamp[idx] = chunk_data.time_stamp;
}
if(!_metadata_got){
if(!onCheckMeta(TitleMeta().getMetadata())){
if (!_metadata_got) {
if (!onCheckMeta(TitleMeta().getMetadata())) {
throw std::runtime_error("onCheckMeta failed");
}
_metadata_got = true;
}
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunkData)));
}
break;
default:
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunk_data)));
break;
}
default: break;
}
}
uint32_t RtmpPlayer::getProgressMilliSecond() const{
uint32_t iTime[2] = {0,0};
for(auto i = 0 ;i < 2 ;i++){
iTime[i] = _aiNowStamp[i] - _aiFistStamp[i];
uint32_t stamp[2] = {0, 0};
for (auto i = 0; i < 2; i++) {
stamp[i] = _now_stamp[i] - _fist_stamp[i];
}
return _iSeekTo + MAX(iTime[0],iTime[1]);
return _seek_ms + MAX(stamp[0], stamp[1]);
}
void RtmpPlayer::seekToMilliSecond(uint32_t seekMS){
if (_bPaused) {
if (_paused) {
pause(false);
}
AMFEncoder enc;
enc << "seek" << ++_iReqID << nullptr << seekMS * 1.0;
enc << "seek" << ++_send_req_id << nullptr << seekMS * 1.0;
sendRequest(MSG_CMD, enc.data());
addOnStatusCB([this,seekMS](AMFValue &val) {
addOnStatusCB([this, seekMS](AMFValue &val) {
//TraceL << "seek result";
_aNowStampTicker[0].resetTime();
_aNowStampTicker[1].resetTime();
_now_stamp_ticker[0].resetTime();
_now_stamp_ticker[1].resetTime();
int iTimeInc = seekMS - getProgressMilliSecond();
for(auto i = 0 ;i < 2 ;i++){
_aiFistStamp[i] = _aiNowStamp[i] + iTimeInc;
_aiNowStamp[i] = _aiFistStamp[i];
for (auto i = 0; i < 2; i++) {
_fist_stamp[i] = _now_stamp[i] + iTimeInc;
_now_stamp[i] = _fist_stamp[i];
}
_iSeekTo = seekMS;
_seek_ms = seekMS;
});
}
} /* namespace mediakit */

View File

@@ -8,8 +8,8 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_RTMP_RtmpPlayer2_H_
#define SRC_RTMP_RtmpPlayer2_H_
#ifndef SRC_RTMP_RtmpPlayer_H_
#define SRC_RTMP_RtmpPlayer_H_
#include <memory>
#include <string>
@@ -28,84 +28,90 @@ using namespace toolkit;
using namespace mediakit::Client;
namespace mediakit {
//实现了rtmp播放器协议部分的功能及数据接收功能
class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{
class RtmpPlayer : public PlayerBase, public TcpClient, public RtmpProtocol {
public:
typedef std::shared_ptr<RtmpPlayer> Ptr;
RtmpPlayer(const EventPoller::Ptr &poller);
virtual ~RtmpPlayer();
~RtmpPlayer() override;
void play(const string &strUrl) override;
void pause(bool bPause) override;
void teardown() override;
protected:
virtual bool onCheckMeta(const AMFValue &val) =0;
virtual void onMediaData(const RtmpPacket::Ptr &chunkData) =0;
virtual bool onCheckMeta(const AMFValue &val) = 0;
virtual void onMediaData(const RtmpPacket::Ptr &chunk_data) = 0;
uint32_t getProgressMilliSecond() const;
void seekToMilliSecond(uint32_t ms);
protected:
void onMediaData_l(const RtmpPacket::Ptr &chunkData);
void onMediaData_l(const RtmpPacket::Ptr &chunk_data);
//在获取config帧后才触发onPlayResult_l(而不是收到play命令回复)所以此时所有track都初始化完毕了
void onPlayResult_l(const SockException &ex, bool handshakeCompleted);
void onPlayResult_l(const SockException &ex, bool handshake_done);
//form Tcpclient
void onRecv(const Buffer::Ptr &pBuf) override;
void onRecv(const Buffer::Ptr &buf) override;
void onConnect(const SockException &err) override;
void onErr(const SockException &ex) override;
//from RtmpProtocol
void onRtmpChunk(RtmpPacket &chunkData) override;
void onStreamDry(uint32_t ui32StreamId) override;
void onSendRawData(const Buffer::Ptr &buffer) override{
void onRtmpChunk(RtmpPacket &chunk_data) override;
void onStreamDry(uint32_t stream_index) override;
void onSendRawData(const Buffer::Ptr &buffer) override {
send(buffer);
}
template<typename FUN>
inline void addOnResultCB(const FUN &fun) {
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
_mapOnResultCB.emplace(_iReqID, fun);
template<typename FUNC>
void addOnResultCB(const FUNC &func) {
lock_guard<recursive_mutex> lck(_mtx_on_result);
_map_on_result.emplace(_send_req_id, func);
}
template<typename FUN>
inline void addOnStatusCB(const FUN &fun) {
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
_dqOnStatusCB.emplace_back(fun);
template<typename FUNC>
void addOnStatusCB(const FUNC &func) {
lock_guard<recursive_mutex> lck(_mtx_on_status);
_deque_on_status.emplace_back(func);
}
void onCmd_result(AMFDecoder &dec);
void onCmd_onStatus(AMFDecoder &dec);
void onCmd_onMetaData(AMFDecoder &dec);
inline void send_connect();
inline void send_createStream();
inline void send_play();
inline void send_pause(bool bPause);
void send_connect();
void send_createStream();
void send_play();
void send_pause(bool pause);
private:
string _strApp;
string _strStream;
string _strTcUrl;
bool _bPaused = false;
string _app;
string _stream_id;
string _tc_url;
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
recursive_mutex _mtxOnResultCB;
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
recursive_mutex _mtxOnStatusCB;
//超时功能实现
Ticker _mediaTicker;
std::shared_ptr<Timer> _pMediaTimer;
std::shared_ptr<Timer> _pPlayTimer;
//心跳定时器
std::shared_ptr<Timer> _pBeatTimer;
//播放进度控制
uint32_t _iSeekTo = 0;
uint32_t _aiFistStamp[2] = { 0, 0 };
uint32_t _aiNowStamp[2] = { 0, 0 };
Ticker _aNowStampTicker[2];
bool _paused = false;
bool _metadata_got = false;
//是否为性能测试模式
bool _benchmark_mode = false;
//播放进度控制
uint32_t _seek_ms = 0;
uint32_t _fist_stamp[2] = {0, 0};
uint32_t _now_stamp[2] = {0, 0};
Ticker _now_stamp_ticker[2];
recursive_mutex _mtx_on_result;
recursive_mutex _mtx_on_status;
deque<function<void(AMFValue &dec)> > _deque_on_status;
unordered_map<int, function<void(AMFDecoder &dec)> > _map_on_result;
//rtmp接收超时计时器
Ticker _rtmp_recv_ticker;
//心跳发送定时器
std::shared_ptr<Timer> _beat_timer;
//播放超时定时器
std::shared_ptr<Timer> _play_timer;
//rtmp接收超时定时器
std::shared_ptr<Timer> _rtmp_recv_timer;
};
} /* namespace mediakit */
#endif /* SRC_RTMP_RtmpPlayer2_H_ */
#endif /* SRC_RTMP_RtmpPlayer_H_ */

View File

@@ -27,51 +27,59 @@ namespace mediakit {
class RtmpPlayerImp: public PlayerImp<RtmpPlayer,RtmpDemuxer> {
public:
typedef std::shared_ptr<RtmpPlayerImp> Ptr;
RtmpPlayerImp(const EventPoller::Ptr &poller) : PlayerImp<RtmpPlayer,RtmpDemuxer>(poller){};
virtual ~RtmpPlayerImp(){
DebugL<<endl;
};
float getProgress() const override{
if(getDuration() > 0){
RtmpPlayerImp(const EventPoller::Ptr &poller) : PlayerImp<RtmpPlayer, RtmpDemuxer>(poller) {};
~RtmpPlayerImp() override {
DebugL << endl;
}
float getProgress() const override {
if (getDuration() > 0) {
return getProgressMilliSecond() / (getDuration() * 1000);
}
return PlayerBase::getProgress();
};
void seekTo(float fProgress) override{
fProgress = MAX(float(0),MIN(fProgress,float(1.0)));
seekToMilliSecond(fProgress * getDuration() * 1000);
};
void play(const string &strUrl) override {
PlayerImp<RtmpPlayer,RtmpDemuxer>::play(strUrl);
}
void seekTo(float fProgress) override {
fProgress = MAX(float(0), MIN(fProgress, float(1.0)));
seekToMilliSecond(fProgress * getDuration() * 1000);
}
void play(const string &strUrl) override {
PlayerImp<RtmpPlayer, RtmpDemuxer>::play(strUrl);
}
private:
//派生类回调函数
bool onCheckMeta(const AMFValue &val) override {
_pRtmpMediaSrc = dynamic_pointer_cast<RtmpMediaSource>(_pMediaSrc);
if(_pRtmpMediaSrc){
_pRtmpMediaSrc->setMetaData(val);
_rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(_pMediaSrc);
if (_rtmp_src) {
_rtmp_src->setMetaData(val);
_set_meta_data = true;
}
_delegate.reset(new RtmpDemuxer);
_delegate->loadMetaData(val);
return true;
}
void onMediaData(const RtmpPacket::Ptr &chunkData) override {
if(_pRtmpMediaSrc){
if(!_set_meta_data && !chunkData->isCfgFrame()){
if (_rtmp_src) {
if (!_set_meta_data && !chunkData->isCfgFrame()) {
_set_meta_data = true;
_pRtmpMediaSrc->setMetaData(TitleMeta().getMetadata());
_rtmp_src->setMetaData(TitleMeta().getMetadata());
}
_pRtmpMediaSrc->onWrite(chunkData);
_rtmp_src->onWrite(chunkData);
}
if(!_delegate){
if (!_delegate) {
//这个流没有metadata
_delegate.reset(new RtmpDemuxer());
}
_delegate->inputRtmp(chunkData);
}
private:
RtmpMediaSource::Ptr _pRtmpMediaSrc;
RtmpMediaSource::Ptr _rtmp_src;
bool _set_meta_data = false;
};

File diff suppressed because it is too large Load Diff

View File

@@ -31,45 +31,42 @@ class RtmpProtocol {
public:
RtmpProtocol();
virtual ~RtmpProtocol();
void onParseRtmp(const char *data, int size);
//作为客户端发送c0c1等待s0s1s2并且回调
void startClientSession(const function<void()> &cb);
void onParseRtmp(const char *pcRawData,int iSize);
void reset();
protected:
virtual void onSendRawData(const Buffer::Ptr &buffer) = 0;
virtual void onRtmpChunk(RtmpPacket &chunkData) = 0;
virtual void onStreamBegin(uint32_t ui32StreamId){
_ui32StreamId = ui32StreamId;
virtual void onRtmpChunk(RtmpPacket &chunk_data) = 0;
virtual void onStreamBegin(uint32_t stream_index){
_stream_index = stream_index;
}
virtual void onStreamEof(uint32_t ui32StreamId){};
virtual void onStreamDry(uint32_t ui32StreamId){};
protected:
void sendAcknowledgement(uint32_t ui32Size);
void sendAcknowledgementSize(uint32_t ui32Size);
void sendPeerBandwidth(uint32_t ui32Size);
void sendChunkSize(uint32_t ui32Size);
void sendPingRequest(uint32_t ui32TimeStamp = ::time(NULL));
void sendPingResponse(uint32_t ui32TimeStamp = ::time(NULL));
void sendSetBufferLength(uint32_t ui32StreamId, uint32_t ui32Length);
void sendUserControl(uint16_t ui16EventType, uint32_t ui32EventData);
void sendUserControl(uint16_t ui16EventType, const string &strEventData);
virtual void onStreamEof(uint32_t stream_index){};
virtual void onStreamDry(uint32_t stream_index){};
void sendInvoke(const string &strCmd, const AMFValue &val);
void sendRequest(int iCmd, const string &str);
void sendResponse(int iType, const string &str);
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const std::string &strBuf, uint32_t ui32TimeStamp, int iChunkID);
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, int iChunkID);
protected:
int _iReqID = 0;
uint32_t _ui32StreamId = STREAM_CONTROL;
int _iNowStreamID = 0;
int _iNowChunkID = 0;
bool _bDataStarted = false;
inline BufferRaw::Ptr obtainBuffer();
inline BufferRaw::Ptr obtainBuffer(const void *data, int len);
//ResourcePool<BufferRaw,MAX_SEND_PKT> _bufferPool;
void reset();
BufferRaw::Ptr obtainBuffer();
BufferRaw::Ptr obtainBuffer(const void *data, int len);
void sendAcknowledgement(uint32_t size);
void sendAcknowledgementSize(uint32_t size);
void sendPeerBandwidth(uint32_t size);
void sendChunkSize(uint32_t size);
void sendPingRequest(uint32_t ti = ::time(NULL));
void sendPingResponse(uint32_t time_stamp = ::time(NULL));
void sendSetBufferLength(uint32_t stream_index, uint32_t len);
void sendUserControl(uint16_t event_type, uint32_t event_data);
void sendUserControl(uint16_t event_type, const string &event_data);
void sendInvoke(const string &cmd, const AMFValue &val);
void sendRequest(int cmd, const string &str);
void sendResponse(int type, const string &str);
void sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id);
void sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::Ptr &buffer, uint32_t stamp, int chunk_id);
private:
void handle_S0S1S2(const function<void()> &cb);
void handle_S0S1S2(const function<void()> &func);
void handle_C0C1();
void handle_C1_simple();
#ifdef ENABLE_OPENSSL
@@ -82,26 +79,32 @@ private:
void handle_C2();
void handle_rtmp();
void handle_rtmpChunk(RtmpPacket &chunkData);
void handle_rtmpChunk(RtmpPacket &chunk_data);
protected:
int _send_req_id = 0;
uint32_t _stream_index = STREAM_CONTROL;
private:
int _now_stream_index = 0;
int _now_chunk_id = 0;
bool _data_started = false;
////////////ChunkSize////////////
size_t _iChunkLenIn = DEFAULT_CHUNK_LEN;
size_t _iChunkLenOut = DEFAULT_CHUNK_LEN;
size_t _chunk_size_in = DEFAULT_CHUNK_LEN;
size_t _chunk_size_out = DEFAULT_CHUNK_LEN;
////////////Acknowledgement////////////
uint32_t _ui32ByteSent = 0;
uint32_t _ui32LastSent = 0;
uint32_t _ui32WinSize = 0;
uint32_t _bytes_sent = 0;
uint32_t _bytes_sent_last = 0;
uint32_t _windows_size = 0;
///////////PeerBandwidth///////////
uint32_t _ui32Bandwidth = 2500000;
uint8_t _ui8LimitType = 2;
////////////Chunk////////////
unordered_map<int, RtmpPacket> _mapChunkData;
uint32_t _bandwidth = 2500000;
uint8_t _band_limit_type = 2;
//////////Rtmp parser//////////
string _strRcvBuf;
function<void()> _nextHandle;
string _recv_data_buf;
function<void()> _next_step_func;
////////////Chunk////////////
unordered_map<int, RtmpPacket> _map_chunk_data;
};
} /* namespace mediakit */
#endif /* SRC_RTMP_RTMPPROTOCOL_H_ */

View File

@@ -18,141 +18,147 @@ using namespace mediakit::Client;
namespace mediakit {
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src) : TcpClient(poller){
_pMediaSrc=src;
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &src) : TcpClient(poller){
_publish_src = src;
}
RtmpPusher::~RtmpPusher() {
teardown();
DebugL << endl;
}
void RtmpPusher::teardown() {
if (alive()) {
_strApp.clear();
_strStream.clear();
_strTcUrl.clear();
_app.clear();
_stream_id.clear();
_tc_url.clear();
{
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
_mapOnResultCB.clear();
lock_guard<recursive_mutex> lck(_mtx_on_result);
_map_on_result.clear();
}
{
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
_dqOnStatusCB.clear();
lock_guard<recursive_mutex> lck(_mtx_on_status);
_deque_on_status.clear();
}
_pPublishTimer.reset();
_publish_timer.reset();
reset();
shutdown(SockException(Err_shutdown,"teardown"));
shutdown(SockException(Err_shutdown, "teardown"));
}
}
void RtmpPusher::onPublishResult(const SockException &ex,bool handshakeCompleted) {
if(!handshakeCompleted){
void RtmpPusher::onPublishResult(const SockException &ex, bool handshake_done) {
if (ex.getErrCode() == Err_shutdown) {
//主动shutdown的不触发回调
return;
}
if (!handshake_done) {
//播放结果回调
_pPublishTimer.reset();
if(_onPublished){
_onPublished(ex);
_publish_timer.reset();
if (_on_published) {
_on_published(ex);
}
} else {
//播放成功后异常断开回调
if(_onShutdown){
_onShutdown(ex);
if (_on_shutdown) {
_on_shutdown(ex);
}
}
if(ex){
if (ex) {
teardown();
}
}
void RtmpPusher::publish(const string &strUrl) {
void RtmpPusher::publish(const string &url) {
teardown();
string strHost = FindField(strUrl.data(), "://", "/");
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
_strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL);
_strTcUrl = string("rtmp://") + strHost + "/" + _strApp;
string host_url = FindField(url.data(), "://", "/");
_app = FindField(url.data(), (host_url + "/").data(), "/");
_stream_id = FindField(url.data(), (host_url + "/" + _app + "/").data(), NULL);
_tc_url = string("rtmp://") + host_url + "/" + _app;
if (!_strApp.size() || !_strStream.size()) {
onPublishResult(SockException(Err_other,"rtmp url非法"),false);
if (!_app.size() || !_stream_id.size()) {
onPublishResult(SockException(Err_other, "rtmp url非法"), false);
return;
}
DebugL << strHost << " " << _strApp << " " << _strStream;
DebugL << host_url << " " << _app << " " << _stream_id;
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
auto iPort = atoi(FindField(host_url.data(), ":", NULL).data());
if (iPort <= 0) {
//rtmp 默认端口1935
iPort = 1935;
} else {
//服务器域名
strHost = FindField(strHost.data(), NULL, ":");
host_url = FindField(host_url.data(), NULL, ":");
}
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
_pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() {
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
_publish_timer.reset(new Timer(publishTimeOutSec, [weakSelf]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return false;
}
strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout"), false);
strongSelf->onPublishResult(SockException(Err_timeout, "publish rtmp timeout"), false);
return false;
},getPoller()));
}, getPoller()));
if(!(*this)[kNetAdapter].empty()){
if (!(*this)[kNetAdapter].empty()) {
setNetAdapter((*this)[kNetAdapter]);
}
startConnect(strHost, iPort);
startConnect(host_url, iPort);
}
void RtmpPusher::onErr(const SockException &ex){
//定时器_pPublishTimer为空后表明握手结束了
onPublishResult(ex,!_pPublishTimer);
onPublishResult(ex, !_publish_timer);
}
void RtmpPusher::onConnect(const SockException &err){
if(err) {
onPublishResult(err,false);
if (err) {
onPublishResult(err, false);
return;
}
//推流器不需要多大的接收缓存,节省内存占用
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
getSock()->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
startClientSession([weakSelf](){
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
weak_ptr<RtmpPusher> weak_self = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
startClientSession([weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strongSelf->sendChunkSize(60000);
strongSelf->send_connect();
strong_self->sendChunkSize(60000);
strong_self->send_connect();
});
}
void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){
void RtmpPusher::onRecv(const Buffer::Ptr &buf){
try {
onParseRtmp(pBuf->data(), pBuf->size());
onParseRtmp(buf->data(), buf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
//定时器_pPublishTimer为空后表明握手结束了
onPublishResult(ex,!_pPublishTimer);
onPublishResult(ex, !_publish_timer);
}
}
inline void RtmpPusher::send_connect() {
AMFValue obj(AMF_OBJECT);
obj.set("app", _strApp);
obj.set("app", _app);
obj.set("type", "nonprivate");
obj.set("tcUrl", _strTcUrl);
obj.set("swfUrl", _strTcUrl);
obj.set("tcUrl", _tc_url);
obj.set("swfUrl", _tc_url);
sendInvoke("connect", obj);
addOnResultCB([this](AMFDecoder &dec){
addOnResultCB([this](AMFDecoder &dec) {
//TraceL << "connect result";
dec.load<AMFValue>();
auto val = dec.load<AMFValue>();
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status"){
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
if (level != "status") {
throw std::runtime_error(StrPrinter << "connect 失败:" << level << " " << code << endl);
}
send_createStream();
});
@@ -161,23 +167,24 @@ inline void RtmpPusher::send_connect() {
inline void RtmpPusher::send_createStream() {
AMFValue obj(AMF_NULL);
sendInvoke("createStream", obj);
addOnResultCB([this](AMFDecoder &dec){
addOnResultCB([this](AMFDecoder &dec) {
//TraceL << "createStream result";
dec.load<AMFValue>();
_ui32StreamId = dec.load<int>();
_stream_index = dec.load<int>();
send_publish();
});
}
inline void RtmpPusher::send_publish() {
AMFEncoder enc;
enc << "publish" << ++_iReqID << nullptr << _strStream << _strApp ;
enc << "publish" << ++_send_req_id << nullptr << _stream_id << _app;
sendRequest(MSG_CMD, enc.data());
addOnStatusCB([this](AMFValue &val) {
auto level = val["level"].as_string();
auto code = val["code"].as_string();
if(level != "status") {
throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
if (level != "status") {
throw std::runtime_error(StrPrinter << "publish 失败:" << level << " " << code << endl);
}
//start send media
send_metaData();
@@ -185,122 +192,124 @@ inline void RtmpPusher::send_publish() {
}
inline void RtmpPusher::send_metaData(){
auto src = _pMediaSrc.lock();
auto src = _publish_src.lock();
if (!src) {
throw std::runtime_error("the media source was released");
}
AMFEncoder enc;
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
enc << "@setDataFrame" << "onMetaData" << src->getMetaData();
sendRequest(MSG_DATA, enc.data());
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
sendRtmp(pkt->typeId, _ui32StreamId, pkt, pkt->timeStamp, pkt->chunkId );
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt) {
sendRtmp(pkt->type_id, _stream_index, pkt, pkt->time_stamp, pkt->chunk_id);
});
_pRtmpReader = src->getRing()->attach(getPoller());
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
_pRtmpReader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt){
auto strongSelf = weakSelf.lock();
if(!strongSelf) {
_rtmp_reader = src->getRing()->attach(getPoller());
weak_ptr<RtmpPusher> weak_self = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
_rtmp_reader->setReadCB([weak_self](const RtmpMediaSource::RingDataType &pkt) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
int i = 0;
int size = pkt->size();
strongSelf->setSendFlushFlag(false);
pkt->for_each([&](const RtmpPacket::Ptr &rtmp){
if(++i == size){
strongSelf->setSendFlushFlag(true);
strong_self->setSendFlushFlag(false);
pkt->for_each([&](const RtmpPacket::Ptr &rtmp) {
if (++i == size) {
strong_self->setSendFlushFlag(true);
}
strongSelf->sendRtmp(rtmp->typeId, strongSelf->_ui32StreamId, rtmp, rtmp->timeStamp, rtmp->chunkId);
strong_self->sendRtmp(rtmp->type_id, strong_self->_stream_index, rtmp, rtmp->time_stamp, rtmp->chunk_id);
});
});
_pRtmpReader->setDetachCB([weakSelf](){
auto strongSelf = weakSelf.lock();
if(strongSelf){
strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer);
_rtmp_reader->setDetachCB([weak_self]() {
auto strong_self = weak_self.lock();
if (strong_self) {
strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer);
}
});
onPublishResult(SockException(Err_success,"success"), false);
onPublishResult(SockException(Err_success, "success"), false);
//提升发送性能
setSocketFlags();
}
void RtmpPusher::setSocketFlags(){
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
if(mergeWriteMS > 0) {
if (mergeWriteMS > 0) {
//提高发送性能
setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
SockUtil::setNoDelay(_sock->rawFD(), false);
SockUtil::setNoDelay(getSock()->rawFD(), false);
}
}
void RtmpPusher::onCmd_result(AMFDecoder &dec){
auto iReqId = dec.load<int>();
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
auto it = _mapOnResultCB.find(iReqId);
if(it != _mapOnResultCB.end()){
auto req_id = dec.load<int>();
lock_guard<recursive_mutex> lck(_mtx_on_result);
auto it = _map_on_result.find(req_id);
if (it != _map_on_result.end()) {
it->second(dec);
_mapOnResultCB.erase(it);
}else{
_map_on_result.erase(it);
} else {
WarnL << "unhandled _result";
}
}
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
AMFValue val;
while(true){
while (true) {
val = dec.load<AMFValue>();
if(val.type() == AMF_OBJECT){
if (val.type() == AMF_OBJECT) {
break;
}
}
if(val.type() != AMF_OBJECT){
if (val.type() != AMF_OBJECT) {
throw std::runtime_error("onStatus:the result object was not found");
}
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
if(_dqOnStatusCB.size()){
_dqOnStatusCB.front()(val);
_dqOnStatusCB.pop_front();
}else{
lock_guard<recursive_mutex> lck(_mtx_on_status);
if (_deque_on_status.size()) {
_deque_on_status.front()(val);
_deque_on_status.pop_front();
} else {
auto level = val["level"];
auto code = val["code"].as_string();
if(level.type() == AMF_STRING){
if(level.as_string() != "status"){
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
if (level.type() == AMF_STRING) {
if (level.as_string() != "status") {
throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl);
}
}
}
}
void RtmpPusher::onRtmpChunk(RtmpPacket &chunkData) {
switch (chunkData.typeId) {
void RtmpPusher::onRtmpChunk(RtmpPacket &chunk_data) {
switch (chunk_data.type_id) {
case MSG_CMD:
case MSG_CMD3: {
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
static onceToken token([]() {
g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
}, []() {});
g_mapCmd.emplace("_error", &RtmpPusher::onCmd_result);
g_mapCmd.emplace("_result", &RtmpPusher::onCmd_result);
g_mapCmd.emplace("onStatus", &RtmpPusher::onCmd_onStatus);
});
AMFDecoder dec(chunkData.strBuf, 0);
AMFDecoder dec(chunk_data.buffer, 0);
std::string type = dec.load<std::string>();
auto it = g_mapCmd.find(type);
if(it != g_mapCmd.end()){
if (it != g_mapCmd.end()) {
auto fun = it->second;
(this->*fun)(dec);
}else{
} else {
WarnL << "can not support cmd:" << type;
}
}
break;
}
default:
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
//WarnL << "unhandled message:" << (int) chunk_data.type_id << hexdump(chunk_data.buffer.data(), chunk_data.buffer.size());
break;
}
}
}

View File

@@ -18,46 +18,47 @@
namespace mediakit {
class RtmpPusher: public RtmpProtocol , public TcpClient , public PusherBase{
class RtmpPusher : public RtmpProtocol, public TcpClient, public PusherBase {
public:
typedef std::shared_ptr<RtmpPusher> Ptr;
RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src);
virtual ~RtmpPusher();
void publish(const string &strUrl) override ;
~RtmpPusher() override;
void publish(const string &url) override ;
void teardown() override;
void setOnPublished(const Event &cb) override {
_onPublished = cb;
_on_published = cb;
}
void setOnShutdown(const Event &cb) override{
_onShutdown = cb;
_on_shutdown = cb;
}
protected:
//for Tcpclient override
void onRecv(const Buffer::Ptr &pBuf) override;
void onRecv(const Buffer::Ptr &buf) override;
void onConnect(const SockException &err) override;
void onErr(const SockException &ex) override;
//for RtmpProtocol override
void onRtmpChunk(RtmpPacket &chunkData) override;
void onRtmpChunk(RtmpPacket &chunk_data) override;
void onSendRawData(const Buffer::Ptr &buffer) override{
send(buffer);
}
private:
void onPublishResult(const SockException &ex,bool handshakeCompleted);
void onPublishResult(const SockException &ex, bool handshake_done);
template<typename FUN>
inline void addOnResultCB(const FUN &fun) {
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
_mapOnResultCB.emplace(_iReqID, fun);
lock_guard<recursive_mutex> lck(_mtx_on_result);
_map_on_result.emplace(_send_req_id, fun);
}
template<typename FUN>
inline void addOnStatusCB(const FUN &fun) {
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
_dqOnStatusCB.emplace_back(fun);
lock_guard<recursive_mutex> lck(_mtx_on_status);
_deque_on_status.emplace_back(fun);
}
void onCmd_result(AMFDecoder &dec);
@@ -69,23 +70,25 @@ private:
inline void send_publish();
inline void send_metaData();
void setSocketFlags();
private:
string _strApp;
string _strStream;
string _strTcUrl;
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
recursive_mutex _mtxOnResultCB;
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
recursive_mutex _mtxOnStatusCB;
//超时功能实现
std::shared_ptr<Timer> _pPublishTimer;
//源
std::weak_ptr<RtmpMediaSource> _pMediaSrc;
RtmpMediaSource::RingType::RingReader::Ptr _pRtmpReader;
private:
string _app;
string _stream_id;
string _tc_url;
recursive_mutex _mtx_on_result;
recursive_mutex _mtx_on_status;
deque<function<void(AMFValue &dec)> > _deque_on_status;
unordered_map<int, function<void(AMFDecoder &dec)> > _map_on_result;
//事件监听
Event _onShutdown;
Event _onPublished;
Event _on_shutdown;
Event _on_published;
//推流超时定时器
std::shared_ptr<Timer> _publish_timer;
std::weak_ptr<RtmpMediaSource> _publish_src;
RtmpMediaSource::RingType::RingReader::Ptr _rtmp_reader;
};
} /* namespace mediakit */

Some files were not shown because too many files have changed in this diff Show More