From 6c9d50b04bb336f03ce3729c7a685041dfad6074 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 24 Oct 2018 12:01:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90H264=20?= =?UTF-8?q?=E7=9A=84=20RTMP=E6=89=93=E5=8C=85=E8=A7=A3=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Player/Frame.h | 15 +++ src/RTP/AACRtpCodec.cpp | 6 +- src/RTP/AACRtpCodec.h | 3 +- src/RTP/H264RtpCodec.cpp | 4 +- src/RTP/H264RtpCodec.h | 3 +- src/RTP/RtpCodec.h | 9 +- src/Rtmp/Rtmp.h | 14 +++ src/RtmpCodec/H264RtmpCodec.cpp | 206 ++++++++++++++++++++++++++++++++ src/RtmpCodec/H264RtmpCodec.h | 70 +++++++++++ src/RtmpCodec/RtmpCodec.cpp | 5 + src/RtmpCodec/RtmpCodec.h | 86 +++++++++++++ 11 files changed, 404 insertions(+), 17 deletions(-) create mode 100644 src/RtmpCodec/H264RtmpCodec.cpp create mode 100644 src/RtmpCodec/H264RtmpCodec.h create mode 100644 src/RtmpCodec/RtmpCodec.cpp create mode 100644 src/RtmpCodec/RtmpCodec.h diff --git a/src/Player/Frame.h b/src/Player/Frame.h index 36f22d98..7b8ecd99 100644 --- a/src/Player/Frame.h +++ b/src/Player/Frame.h @@ -65,6 +65,21 @@ public: virtual bool keyFrame() const = 0; }; +template +class ResourcePoolHelper{ +public: + ResourcePoolHelper(int size = 8){ + _pool.setSize(size); + } + virtual ~ResourcePoolHelper(){} + + std::shared_ptr obtainObj(){ + return _pool.obtain(); + } +private: + ResourcePool _pool; +}; + /** * 帧环形缓存接口类 */ diff --git a/src/RTP/AACRtpCodec.cpp b/src/RTP/AACRtpCodec.cpp index 43455762..59136c0c 100644 --- a/src/RTP/AACRtpCodec.cpp +++ b/src/RTP/AACRtpCodec.cpp @@ -55,8 +55,8 @@ void AACRtpEncoder::makeAACRtp(const void *pData, unsigned int uiLen, bool bMark uint32_t ts = htonl(m_ui32TimeStamp); uint16_t sq = htons(m_ui16Sequence); uint32_t sc = htonl(m_ui32Ssrc); - auto pRtppkt = obtainRtp(); - auto &rtppkt = *(pRtppkt.get()); + auto pRtppkt = ResourcePoolHelper::obtainObj(); + auto &rtppkt = *pRtppkt; unsigned char *pucRtp = rtppkt.payload; pucRtp[0] = '$'; pucRtp[1] = m_ui8Interleaved; @@ -93,7 +93,7 @@ AACRtpDecoder::AACRtpDecoder(uint32_t ui32SampleRate) { AACFrame::Ptr AACRtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 - auto frame = m_framePool.obtain(); + auto frame = ResourcePoolHelper::obtainObj(); frame->aac_frame_length = 7; frame->iPrefixSize = 7; return frame; diff --git a/src/RTP/AACRtpCodec.h b/src/RTP/AACRtpCodec.h index 148560a9..9d56a449 100644 --- a/src/RTP/AACRtpCodec.h +++ b/src/RTP/AACRtpCodec.h @@ -10,7 +10,7 @@ /** * aac rtp转adts类 */ -class AACRtpDecoder : public RtpCodec { +class AACRtpDecoder : public RtpCodec , public ResourcePoolHelper { public: typedef std::shared_ptr Ptr; @@ -39,7 +39,6 @@ private: AACFrame::Ptr obtainFrame(); private: AACFrame::Ptr m_adts; - ResourcePool m_framePool; uint32_t m_sampleRate; }; diff --git a/src/RTP/H264RtpCodec.cpp b/src/RTP/H264RtpCodec.cpp index 9e0bede3..4215034b 100644 --- a/src/RTP/H264RtpCodec.cpp +++ b/src/RTP/H264RtpCodec.cpp @@ -11,7 +11,7 @@ H264RtpDecoder::H264RtpDecoder() { H264Frame::Ptr H264RtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 - auto frame = m_framePool.obtain(); + auto frame = ResourcePoolHelper::obtainObj(); frame->buffer.clear(); frame->iPrefixSize = 4; return frame; @@ -174,7 +174,7 @@ void H264RtpEncoder::makeH264Rtp(const void* data, unsigned int len, bool mark, uint16_t sq = htons(m_ui16Sequence); uint32_t sc = htonl(m_ui32Ssrc); - auto rtppkt = obtainRtp(); + auto rtppkt = ResourcePoolHelper::obtainObj(); unsigned char *pucRtp = rtppkt->payload; pucRtp[0] = '$'; pucRtp[1] = m_ui8Interleaved; diff --git a/src/RTP/H264RtpCodec.h b/src/RTP/H264RtpCodec.h index e0adcded..5c1f3735 100644 --- a/src/RTP/H264RtpCodec.h +++ b/src/RTP/H264RtpCodec.h @@ -13,7 +13,7 @@ using namespace ZL::Util; /** * h264 rtp解码类 */ -class H264RtpDecoder : public RtpCodec { +class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper { public: typedef std::shared_ptr Ptr; @@ -40,7 +40,6 @@ private: H264Frame::Ptr obtainFrame(); private: H264Frame::Ptr m_h264frame; - ResourcePool m_framePool; }; /** diff --git a/src/RTP/RtpCodec.h b/src/RTP/RtpCodec.h index 6001b297..ac49dd46 100644 --- a/src/RTP/RtpCodec.h +++ b/src/RTP/RtpCodec.h @@ -72,7 +72,6 @@ public: typedef std::shared_ptr Ptr; RtpRing(){ - //禁用缓存 _rtpRing = std::make_shared(); } virtual ~RtpRing(){} @@ -111,7 +110,6 @@ public: m_ui32MtuSize = ui32MtuSize; m_ui8PlayloadType = ui8PlayloadType; m_ui8Interleaved = ui8Interleaved; - m_rtpPool.setSize(32); } virtual ~RtpInfo(){} @@ -141,10 +139,6 @@ public: uint32_t getMtuSize() const { return m_ui32MtuSize; } -protected: - RtpPacket::Ptr obtainRtp(){ - return m_rtpPool.obtain(); - } protected: uint32_t m_ui32Ssrc; uint32_t m_ui32SampleRate; @@ -153,10 +147,9 @@ protected: uint8_t m_ui8Interleaved; uint16_t m_ui16Sequence = 0; uint32_t m_ui32TimeStamp = 0; - ResourcePool m_rtpPool; }; -class RtpCodec : public RtpRing, public FrameRingInterfaceDelegate , public CodecInfo{ +class RtpCodec : public RtpRing, public FrameRingInterfaceDelegate , public CodecInfo , public ResourcePoolHelper{ public: typedef std::shared_ptr Ptr; RtpCodec(){} diff --git a/src/Rtmp/Rtmp.h b/src/Rtmp/Rtmp.h index d253de5a..af20b58a 100644 --- a/src/Rtmp/Rtmp.h +++ b/src/Rtmp/Rtmp.h @@ -188,6 +188,11 @@ public: const static int channel[] = { 1, 2 }; return channel[flvStereoOrMono]; } + + /** + * 返回不带0x00 00 00 01头的sps + * @return + */ string getH264SPS() const { string ret; if (getMediaType() != 7) { @@ -210,6 +215,11 @@ public: ret.assign(strBuf.data() + 13, sps_size); return ret; } + + /** + * 返回不带0x00 00 00 01头的pps + * @return + */ string getH264PPS() const { string ret; if (getMediaType() != 7) { @@ -258,4 +268,8 @@ public: } }; + + + + #endif diff --git a/src/RtmpCodec/H264RtmpCodec.cpp b/src/RtmpCodec/H264RtmpCodec.cpp new file mode 100644 index 00000000..21456d63 --- /dev/null +++ b/src/RtmpCodec/H264RtmpCodec.cpp @@ -0,0 +1,206 @@ +// +// Created by xzl on 2018/10/18. +// + +#include "H264RtmpCodec.h" + + +H264RtmpDecoder::H264RtmpDecoder() { + m_h264frame = obtainFrame(); +} + +H264Frame::Ptr H264RtmpDecoder::obtainFrame() { + //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 + auto frame = obtainObj(); + frame->buffer.clear(); + frame->iPrefixSize = 4; + return frame; +} + +bool H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) { + key_pos = decodeRtmp(rtmp); + RtmpCodec::inputRtmp(rtmp, key_pos); + return key_pos; +} + +bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) { + if (pkt->isCfgFrame()) { + //缓存sps pps,后续插入到I帧之前 + m_strSPS = pkt->getH264SPS(); + m_strPPS = pkt->getH264PPS(); + return false; + } + + if (m_strSPS.size()) { + uint32_t iTotalLen = pkt->strBuf.size(); + uint32_t iOffset = 5; + while(iOffset + 4 < iTotalLen){ + uint32_t iFrameLen; + memcpy(&iFrameLen, pkt->strBuf.data() + iOffset, 4); + iFrameLen = ntohl(iFrameLen); + iOffset += 4; + if(iFrameLen + iOffset > iTotalLen){ + break; + } + onGetH264_l(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp); + iOffset += iFrameLen; + } + } + return pkt->isVideoKeyFrame(); +} + + +inline void H264RtmpDecoder::onGetH264_l(const char* pcData, int iLen, uint32_t ui32TimeStamp) { + switch (pcData[0] & 0x1F) { + case 5: { + //I frame + onGetH264(m_strSPS.data(), m_strSPS.length(), ui32TimeStamp); + onGetH264(m_strPPS.data(), m_strPPS.length(), ui32TimeStamp); + } + case 1: { + //I or P or B frame + onGetH264(pcData, iLen, ui32TimeStamp); + } + break; + default: + //WarnL <<(int)(pcData[0] & 0x1F); + break; + } +} +inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t ui32TimeStamp) { + m_h264frame->type = pcData[0] & 0x1F; + m_h264frame->timeStamp = ui32TimeStamp; + m_h264frame->buffer.assign("\x0\x0\x0\x1", 4); //添加264头 + m_h264frame->buffer.append(pcData, iLen); + + //写入环形缓存 + RtmpCodec::inputFrame(m_h264frame); + m_h264frame = obtainFrame(); +} + + + +//////////////////////////////////////////////////////////////////////// + +H264RtmpEncoder::H264RtmpEncoder() { +} + +void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { + RtmpCodec::inputFrame(frame); + + auto pcData = frame->data() + frame->prefixSize(); + auto iLen = frame->size() - frame->prefixSize(); + auto type = ((uint8_t*)pcData)[0] & 0x1F; + + switch (type){ + case 7:{ + //sps + if(m_sps.empty()){ + m_sps = string(pcData,iLen); + if(!m_pps.empty()){ + makeVideoConfigPkt(); + } + } + } + break; + case 8:{ + //pps + if(m_pps.empty()){ + m_pps = string(pcData,iLen); + if(!m_sps.empty()){ + makeVideoConfigPkt(); + } + } + } + break; + case 1: + case 5:{ + //I or P or B frame + int8_t flags = 7; //h.264 + bool is_config = false; + bool keyFrame = frame->keyFrame(); + flags |= ((keyFrame ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); + + RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); + rtmpPkt->strBuf.push_back(flags); + rtmpPkt->strBuf.push_back(!is_config); + rtmpPkt->strBuf.append("\x0\x0\x0", 3); + auto size = htonl(iLen); + rtmpPkt->strBuf.append((char *) &size, 4); + rtmpPkt->strBuf.append(pcData, iLen); + + rtmpPkt->bodySize = rtmpPkt->strBuf.size(); + rtmpPkt->chunkId = CHUNK_VIDEO; + rtmpPkt->streamId = STREAM_MEDIA; + rtmpPkt->timeStamp = frame->stamp(); + rtmpPkt->typeId = MSG_VIDEO; + RtmpCodec::inputRtmp(rtmpPkt,keyFrame); + } + break; + + default: + break; + } +} + + +void H264RtmpEncoder::makeVideoConfigPkt() { + int8_t flags = 7; //h.264 + flags |= (FLV_KEY_FRAME << 4); + bool is_config = true; + + RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); + //////////header + rtmpPkt->strBuf.push_back(flags); + rtmpPkt->strBuf.push_back(!is_config); + rtmpPkt->strBuf.append("\x0\x0\x0", 3); + + ////////////sps + rtmpPkt->strBuf.push_back(1); // version + + //DebugL<strBuf.push_back(m_sps[1]); // profile + rtmpPkt->strBuf.push_back(m_sps[2]); // compat + rtmpPkt->strBuf.push_back(m_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) + uint16_t size = m_sps.size(); + size = htons(size); + rtmpPkt->strBuf.append((char *) &size, 2); + rtmpPkt->strBuf.append(m_sps); + + /////////////pps + rtmpPkt->strBuf.push_back(1); // version + size = m_pps.size(); + size = htons(size); + rtmpPkt->strBuf.append((char *) &size, 2); + rtmpPkt->strBuf.append(m_pps); + + rtmpPkt->bodySize = rtmpPkt->strBuf.size(); + rtmpPkt->chunkId = CHUNK_VIDEO; + rtmpPkt->streamId = STREAM_MEDIA; + rtmpPkt->timeStamp = 0; + rtmpPkt->typeId = MSG_VIDEO; + RtmpCodec::inputRtmp(rtmpPkt, true); +} + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RtmpCodec/H264RtmpCodec.h b/src/RtmpCodec/H264RtmpCodec.h new file mode 100644 index 00000000..aa0cf49f --- /dev/null +++ b/src/RtmpCodec/H264RtmpCodec.h @@ -0,0 +1,70 @@ +// +// Created by xzl on 2018/10/24. +// + +#ifndef ZLMEDIAKIT_H264RTMPCODEC_H +#define ZLMEDIAKIT_H264RTMPCODEC_H + +#include "RtmpCodec.h" +#include "Util/ResourcePool.h" + +using namespace ZL::Rtmp; + +/** + * h264 Rtmp解码类 + */ +class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + H264RtmpDecoder(); + ~H264RtmpDecoder() {} + + /** + * 输入264 Rtmp包 + * @param rtmp Rtmp包 + * @param key_pos 此参数忽略之 + */ + bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override; + + TrackType getTrackType() const override{ + return TrackVideo; + } + + CodecId getCodecId() const override{ + return CodecH264; + } +private: + bool decodeRtmp(const RtmpPacket::Ptr &Rtmp); + void onGetH264_l(const char *pcData, int iLen, uint32_t ui32TimeStamp); + void onGetH264(const char *pcData, int iLen, uint32_t ui32TimeStamp); + H264Frame::Ptr obtainFrame(); +private: + H264Frame::Ptr m_h264frame; + string m_strSPS; + string m_strPPS; +}; + +/** + * 264 Rtmp打包类 + */ +class H264RtmpEncoder : public H264RtmpDecoder, public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + H264RtmpEncoder(); + ~H264RtmpEncoder() {} + + /** + * 输入264帧,需要指出的是,必须输入sps pps帧 + * @param frame 帧数据 + */ + void inputFrame(const Frame::Ptr &frame) override; +private: + void makeVideoConfigPkt(); +private: + string m_sps; + string m_pps; +}; + +#endif //ZLMEDIAKIT_H264RTMPCODEC_H diff --git a/src/RtmpCodec/RtmpCodec.cpp b/src/RtmpCodec/RtmpCodec.cpp new file mode 100644 index 00000000..d3556478 --- /dev/null +++ b/src/RtmpCodec/RtmpCodec.cpp @@ -0,0 +1,5 @@ +// +// Created by xzl on 2018/10/24. +// + +#include "RtmpCodec.h" diff --git a/src/RtmpCodec/RtmpCodec.h b/src/RtmpCodec/RtmpCodec.h new file mode 100644 index 00000000..ed9a6662 --- /dev/null +++ b/src/RtmpCodec/RtmpCodec.h @@ -0,0 +1,86 @@ +// +// Created by xzl on 2018/10/24. +// + +#ifndef ZLMEDIAKIT_RTMPCODEC_H +#define ZLMEDIAKIT_RTMPCODEC_H + +#include "Rtmp/Rtmp.h" +#include "Player/Frame.h" +#include "Util/RingBuffer.h" + +using namespace ZL::Util; + +namespace ZL{ +namespace Rtmp { + + +class RtmpRingInterface { +public: + typedef RingBuffer RingType; + typedef std::shared_ptr Ptr; + + RtmpRingInterface(){} + virtual ~RtmpRingInterface(){} + + /** + * 获取rtmp环形缓存 + * @return + */ + virtual RingType::Ptr getRtmpRing() const = 0; + + /** + * 设置rtmp环形缓存 + * @param ring + */ + virtual void setRtmpRing(const RingType::Ptr &ring) = 0; + + /** + * 输入rtmp包 + * @param rtmp rtmp包 + * @param key_pos 是否为关键帧 + * @return 是否为关键帧 + */ + virtual bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) = 0; +}; + +class RtmpRing : public RtmpRingInterface { +public: + typedef std::shared_ptr Ptr; + + RtmpRing(){ + _rtmpRing = std::make_shared(); + } + virtual ~RtmpRing(){} + + RingType::Ptr getRtmpRing() const override { + return _rtmpRing; + } + + void setRtmpRing(const RingType::Ptr &ring) override { + _rtmpRing = ring; + } + + bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) override{ + _rtmpRing->write(rtmp,key_pos); + return key_pos; + } +protected: + RingType::Ptr _rtmpRing; +}; + + +class RtmpCodec : public RtmpRing, public FrameRingInterfaceDelegate , public CodecInfo{ +public: + typedef std::shared_ptr Ptr; + RtmpCodec(){} + virtual ~RtmpCodec(){} +}; + + + + +}//namespace Rtmp +}//namespace ZL + +#endif //ZLMEDIAKIT_RTMPCODEC_H