mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-10-31 00:37:39 +08:00
commit
ad83b39b0f
@ -1 +1 @@
|
||||
Subproject commit fe572323b10d72819a4d69b326dd70e73c7bf1a6
|
||||
Subproject commit 936d3c05b183cba279bb348f8eac9eca0cc810c2
|
@ -128,13 +128,14 @@ public:
|
||||
* 一个复合帧可以通过无内存拷贝的方式切割成多个H264FrameSubFrame
|
||||
* 提供该类的目的是切换复合帧时防止内存拷贝,提高性能
|
||||
*/
|
||||
class H264FrameSubFrame : public H264FrameNoCacheAble{
|
||||
template<typename Parent>
|
||||
class FrameInternal : public Parent{
|
||||
public:
|
||||
typedef std::shared_ptr<H264FrameSubFrame> Ptr;
|
||||
H264FrameSubFrame(const Frame::Ptr &parent_frame,
|
||||
char *ptr,
|
||||
uint32_t size,
|
||||
int prefixeSize) : H264FrameNoCacheAble(ptr,size,parent_frame->dts(),parent_frame->pts(),prefixeSize){
|
||||
typedef std::shared_ptr<FrameInternal> Ptr;
|
||||
FrameInternal(const Frame::Ptr &parent_frame,
|
||||
char *ptr,
|
||||
uint32_t size,
|
||||
int prefixeSize) : Parent(ptr,size,parent_frame->dts(),parent_frame->pts(),prefixeSize){
|
||||
_parent_frame = parent_frame;
|
||||
}
|
||||
bool cacheAble() const override {
|
||||
@ -144,6 +145,8 @@ private:
|
||||
Frame::Ptr _parent_frame;
|
||||
};
|
||||
|
||||
typedef FrameInternal<H264FrameNoCacheAble> H264FrameInternal;
|
||||
|
||||
/**
|
||||
* 264视频通道
|
||||
*/
|
||||
@ -243,21 +246,21 @@ public:
|
||||
splitH264(frame->data() + frame->prefixSize(),
|
||||
frame->size() - frame->prefixSize(),
|
||||
[&](const char *ptr, int len){
|
||||
if(first_frame){
|
||||
H264FrameSubFrame::Ptr sub_frame = std::make_shared<H264FrameSubFrame>(frame,
|
||||
frame->data(),
|
||||
len + frame->prefixSize(),
|
||||
frame->prefixSize());
|
||||
inputFrame_l(sub_frame);
|
||||
first_frame = false;
|
||||
}else{
|
||||
H264FrameSubFrame::Ptr sub_frame = std::make_shared<H264FrameSubFrame>(frame,
|
||||
(char *)ptr,
|
||||
len ,
|
||||
3);
|
||||
inputFrame_l(sub_frame);
|
||||
}
|
||||
});
|
||||
if(first_frame){
|
||||
H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame,
|
||||
frame->data(),
|
||||
len + frame->prefixSize(),
|
||||
frame->prefixSize());
|
||||
inputFrame_l(sub_frame);
|
||||
first_frame = false;
|
||||
}else{
|
||||
H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame,
|
||||
(char *)ptr,
|
||||
len ,
|
||||
3);
|
||||
inputFrame_l(sub_frame);
|
||||
}
|
||||
});
|
||||
} else{
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) {
|
||||
//缓存sps pps,后续插入到I帧之前
|
||||
_sps = pkt->getH264SPS();
|
||||
_pps = pkt->getH264PPS();
|
||||
onGetH264(_sps.data(), _sps.size(), pkt->timeStamp , pkt->timeStamp);
|
||||
onGetH264(_pps.data(), _pps.size(), pkt->timeStamp , pkt->timeStamp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -69,44 +71,13 @@ bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) {
|
||||
if(iFrameLen + iOffset > iTotalLen){
|
||||
break;
|
||||
}
|
||||
onGetH264_l(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp , pts);
|
||||
onGetH264(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp , pts);
|
||||
iOffset += iFrameLen;
|
||||
}
|
||||
}
|
||||
return pkt->isVideoKeyFrame();
|
||||
}
|
||||
|
||||
|
||||
inline void H264RtmpDecoder::onGetH264_l(const char* pcData, int iLen, uint32_t dts,uint32_t pts) {
|
||||
switch (H264_TYPE(pcData[0])) {
|
||||
case H264Frame::NAL_IDR: {
|
||||
//I frame
|
||||
if(_sps.length()){
|
||||
onGetH264(_sps.data(), _sps.length(), dts , pts);
|
||||
}
|
||||
if(_pps.length()){
|
||||
onGetH264(_pps.data(), _pps.length(), dts , pts);
|
||||
}
|
||||
onGetH264(pcData, iLen, dts , pts);
|
||||
}
|
||||
break;
|
||||
case H264Frame::NAL_B_P: {
|
||||
//I or P or B frame
|
||||
onGetH264(pcData, iLen, dts , pts);
|
||||
}
|
||||
break;
|
||||
case H264Frame::NAL_SPS: {
|
||||
_sps.assign(pcData, iLen);
|
||||
}
|
||||
break;
|
||||
case H264Frame::NAL_PPS:{
|
||||
_pps.assign(pcData, iLen);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dts,uint32_t pts) {
|
||||
#if 1
|
||||
_h264frame->type = H264_TYPE(pcData[0]);
|
||||
|
@ -60,7 +60,6 @@ public:
|
||||
}
|
||||
protected:
|
||||
bool decodeRtmp(const RtmpPacket::Ptr &Rtmp);
|
||||
void onGetH264_l(const char *pcData, int iLen, uint32_t dts,uint32_t pts);
|
||||
void onGetH264(const char *pcData, int iLen, uint32_t dts,uint32_t pts);
|
||||
H264Frame::Ptr obtainFrame();
|
||||
protected:
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "Frame.h"
|
||||
#include "Track.h"
|
||||
#include "Util/base64.h"
|
||||
#include "H264.h"
|
||||
using namespace toolkit;
|
||||
#define H265_TYPE(v) (((uint8_t)(v) >> 1) & 0x3f)
|
||||
|
||||
@ -147,6 +148,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
typedef FrameInternal<H265FrameNoCacheAble> H265FrameInternal;
|
||||
|
||||
/**
|
||||
* 265视频通道
|
||||
@ -209,11 +211,38 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 输入数据帧,并获取sps pps
|
||||
* @param frame 数据帧
|
||||
*/
|
||||
void inputFrame(const Frame::Ptr &frame) override{
|
||||
bool first_frame = true;
|
||||
splitH264(frame->data() + frame->prefixSize(),
|
||||
frame->size() - frame->prefixSize(),
|
||||
[&](const char *ptr, int len){
|
||||
if(first_frame){
|
||||
H265FrameInternal::Ptr sub_frame = std::make_shared<H265FrameInternal>(frame,
|
||||
frame->data(),
|
||||
len + frame->prefixSize(),
|
||||
frame->prefixSize());
|
||||
inputFrame_l(sub_frame);
|
||||
first_frame = false;
|
||||
}else{
|
||||
H265FrameInternal::Ptr sub_frame = std::make_shared<H265FrameInternal>(frame,
|
||||
(char *)ptr,
|
||||
len ,
|
||||
3);
|
||||
inputFrame_l(sub_frame);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* 输入数据帧,并获取sps pps
|
||||
* @param frame 数据帧
|
||||
*/
|
||||
void inputFrame(const Frame::Ptr &frame) override {
|
||||
void inputFrame_l(const Frame::Ptr &frame) {
|
||||
int type = H265_TYPE(((uint8_t *) frame->data() + frame->prefixSize())[0]);
|
||||
if (H265Frame::isKeyFrame(type)) {
|
||||
insertConfigFrame(frame);
|
||||
@ -250,7 +279,7 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
Track::Ptr clone() override {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type>(*this);
|
||||
}
|
||||
|
@ -134,7 +134,10 @@ int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//默认后面数据不是content而是header
|
||||
//跨域
|
||||
_origin = _parser["Origin"];
|
||||
|
||||
//默认后面数据不是content而是header
|
||||
int64_t content_len = 0;
|
||||
auto &fun = it->second;
|
||||
try {
|
||||
@ -775,6 +778,11 @@ inline HttpSession::KeyValue HttpSession::makeHttpHeader(bool bClose, int64_t iC
|
||||
if(iContentSize > 0){
|
||||
headerOut.emplace("Content-Length", StrPrinter<<iContentSize<<endl);
|
||||
}
|
||||
|
||||
if(!_origin.empty()){
|
||||
headerOut.emplace("Access-Control-Allow-Origin",_origin);
|
||||
headerOut.emplace("Access-Control-Allow-Credentials", "true");
|
||||
}
|
||||
return headerOut;
|
||||
}
|
||||
|
||||
@ -802,20 +810,19 @@ inline bool HttpSession::emitHttpEvent(bool doInvoke){
|
||||
GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount);
|
||||
|
||||
bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt);
|
||||
auto Origin = _parser["Origin"];
|
||||
/////////////////////异步回复Invoker///////////////////////////////
|
||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||
HttpResponseInvoker invoker = [weakSelf,bClose,Origin](const string &codeOut, const KeyValue &headerOut, const string &contentOut){
|
||||
HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut, const KeyValue &headerOut, const string &contentOut){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->async([weakSelf,bClose,codeOut,headerOut,contentOut,Origin]() {
|
||||
strongSelf->async([weakSelf,bClose,codeOut,headerOut,contentOut]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->responseDelay(Origin,bClose,codeOut,headerOut,contentOut);
|
||||
strongSelf->responseDelay(bClose,codeOut,headerOut,contentOut);
|
||||
if(bClose){
|
||||
strongSelf->shutdown(SockException(Err_shutdown,"Connection: close"));
|
||||
}
|
||||
@ -906,19 +913,15 @@ inline void HttpSession::Handle_Req_POST(int64_t &content_len) {
|
||||
}
|
||||
//有后续content数据要处理,暂时不关闭连接
|
||||
}
|
||||
void HttpSession::responseDelay(const string &Origin,bool bClose,
|
||||
const string &codeOut,const KeyValue &headerOut,
|
||||
void HttpSession::responseDelay(bool bClose,
|
||||
const string &codeOut,
|
||||
const KeyValue &headerOut,
|
||||
const string &contentOut){
|
||||
if(codeOut.empty()){
|
||||
sendNotFound(bClose);
|
||||
return;
|
||||
}
|
||||
auto headerOther=makeHttpHeader(bClose,contentOut.size(),"text/plain");
|
||||
if(!Origin.empty()){
|
||||
headerOther["Access-Control-Allow-Origin"] = Origin;
|
||||
headerOther["Access-Control-Allow-Credentials"] = "true";
|
||||
}
|
||||
|
||||
auto headerOther = makeHttpHeader(bClose,contentOut.size(),"text/plain");
|
||||
for (auto &pr : headerOther){
|
||||
//添加默认http头,默认http头不能覆盖用户自定义的头
|
||||
const_cast<KeyValue &>(headerOut).emplace(pr.first,pr.second);
|
||||
|
@ -111,9 +111,8 @@ private:
|
||||
inline void urlDecode(Parser &parser);
|
||||
inline void sendNotFound(bool bClose);
|
||||
inline void sendResponse(const char *pcStatus,const KeyValue &header,const string &strContent);
|
||||
inline static KeyValue makeHttpHeader(bool bClose=false,int64_t iContentSize=-1,const char *pcContentType="text/html");
|
||||
void responseDelay(const string &Origin,
|
||||
bool bClose,
|
||||
inline KeyValue makeHttpHeader(bool bClose=false,int64_t iContentSize=-1,const char *pcContentType="text/html");
|
||||
void responseDelay(bool bClose,
|
||||
const string &codeOut,
|
||||
const KeyValue &headerOut,
|
||||
const string &contentOut);
|
||||
@ -139,6 +138,7 @@ private:
|
||||
*/
|
||||
inline string getClientUid();
|
||||
private:
|
||||
string _origin;
|
||||
Parser _parser;
|
||||
Ticker _ticker;
|
||||
uint32_t _iReqCnt = 0;
|
||||
|
@ -245,14 +245,9 @@ protected:
|
||||
_playResultCB = nullptr;
|
||||
return;
|
||||
}
|
||||
//播放成功后,我们还必须等待各个Track初始化完毕才能回调告知已经初始化完毕
|
||||
if(isInited(0xFFFF)){
|
||||
//初始化完毕则立即回调
|
||||
_playResultCB(ex);
|
||||
_playResultCB = nullptr;
|
||||
return;
|
||||
}
|
||||
//播放成功却未初始化完毕,这个时候不回调汇报播放成功
|
||||
//播放成功
|
||||
_playResultCB(ex);
|
||||
_playResultCB = nullptr;
|
||||
}
|
||||
|
||||
void onResume() override{
|
||||
@ -260,16 +255,6 @@ protected:
|
||||
_resumeCB();
|
||||
}
|
||||
}
|
||||
|
||||
void checkInited(int analysisMs){
|
||||
if(!_playResultCB){
|
||||
return;
|
||||
}
|
||||
if(isInited(analysisMs)){
|
||||
_playResultCB(SockException(Err_success,"play success"));
|
||||
_playResultCB = nullptr;
|
||||
}
|
||||
}
|
||||
protected:
|
||||
function<void(const SockException &ex)> _shutdownCB;
|
||||
function<void(const SockException &ex)> _playResultCB;
|
||||
|
@ -321,7 +321,6 @@ void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) {
|
||||
if(!onCheckMeta(val)){
|
||||
throw std::runtime_error("onCheckMeta faied");
|
||||
}
|
||||
onPlayResult_l(SockException(Err_success,"play rtmp success"));
|
||||
}
|
||||
|
||||
void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) {
|
||||
@ -329,6 +328,25 @@ void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) {
|
||||
onPlayResult_l(SockException(Err_other,"rtmp stream dry"));
|
||||
}
|
||||
|
||||
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) {
|
||||
_mediaTicker.resetTime();
|
||||
if(!_pPlayTimer){
|
||||
//已经触发了onPlayResult事件,直接触发onMediaData事件
|
||||
onMediaData(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if(packet->isCfgFrame()){
|
||||
//输入配置帧以便初始化完成各个track
|
||||
onMediaData(packet);
|
||||
}else{
|
||||
//先触发onPlayResult事件,这个时候解码器才能初始化完毕
|
||||
onPlayResult_l(SockException(Err_success,"play rtmp success"));
|
||||
//触发onPlayResult事件后,再把帧数据输入到解码器
|
||||
onMediaData(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
@ -351,6 +369,7 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
case MSG_VIDEO: {
|
||||
auto idx = chunkData.typeId%2;
|
||||
if (_aNowStampTicker[idx].elapsedTime() > 500) {
|
||||
//计算播放进度时间轴用
|
||||
_aiNowStamp[idx] = chunkData.timeStamp;
|
||||
}
|
||||
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunkData)));
|
||||
|
@ -60,11 +60,7 @@ protected:
|
||||
uint32_t getProgressMilliSecond() const;
|
||||
void seekToMilliSecond(uint32_t ms);
|
||||
protected:
|
||||
void onMediaData_l(const RtmpPacket::Ptr &chunkData) {
|
||||
_mediaTicker.resetTime();
|
||||
onMediaData(chunkData);
|
||||
}
|
||||
|
||||
void onMediaData_l(const RtmpPacket::Ptr &chunkData);
|
||||
void onPlayResult_l(const SockException &ex);
|
||||
|
||||
//for Tcpclient
|
||||
@ -97,7 +93,7 @@ protected:
|
||||
inline void send_createStream();
|
||||
inline void send_play();
|
||||
inline void send_pause(bool bPause);
|
||||
|
||||
private:
|
||||
string _strApp;
|
||||
string _strStream;
|
||||
string _strTcUrl;
|
||||
|
@ -58,7 +58,6 @@ public:
|
||||
seekToMilliSecond(fProgress * getDuration() * 1000);
|
||||
};
|
||||
void play(const string &strUrl) override {
|
||||
_analysisMs = (*this)[kMaxAnalysisMS].as<int>();
|
||||
PlayerImp<RtmpPlayer,RtmpDemuxer>::play(strUrl);
|
||||
}
|
||||
private:
|
||||
@ -76,16 +75,13 @@ private:
|
||||
_pRtmpMediaSrc->onWrite(chunkData);
|
||||
}
|
||||
if(!_parser){
|
||||
//这个流没有metedata,那么尝试在音视频包里面还原出相关信息
|
||||
//这个流没有metedata
|
||||
_parser.reset(new RtmpDemuxer());
|
||||
onPlayResult_l(SockException(Err_success,"play rtmp success"));
|
||||
}
|
||||
_parser->inputRtmp(chunkData);
|
||||
checkInited(_analysisMs);
|
||||
}
|
||||
private:
|
||||
RtmpMediaSource::Ptr _pRtmpMediaSrc;
|
||||
int _analysisMs;
|
||||
};
|
||||
|
||||
|
||||
|
@ -39,9 +39,9 @@ using namespace std;
|
||||
using namespace toolkit;
|
||||
using namespace mediakit;
|
||||
|
||||
std::string Utf8ToGbk(std::string src_str)
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::string Utf8ToGbk(std::string src_str){
|
||||
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, src_str.c_str(), -1, NULL, 0);
|
||||
wchar_t* wszGBK = new wchar_t[len + 1];
|
||||
memset(wszGBK, 0, len * 2 + 2);
|
||||
@ -54,9 +54,6 @@ std::string Utf8ToGbk(std::string src_str)
|
||||
if (wszGBK) delete[] wszGBK;
|
||||
if (szGBK) delete[] szGBK;
|
||||
return strTemp;
|
||||
#else
|
||||
return src_str;
|
||||
#endif
|
||||
}
|
||||
|
||||
class log4Channel : public LogChannel {
|
||||
@ -75,6 +72,9 @@ public:
|
||||
printf("%s %s\n", logContext->_function, Utf8ToGbk(logContext->str()).c_str());
|
||||
}
|
||||
};
|
||||
#else
|
||||
typedef ConsoleChannel log4Channel;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <TCHAR.h>
|
||||
|
Loading…
Reference in New Issue
Block a user