diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 13acc97e..62549d05 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -30,6 +30,8 @@ #include "Frame.h" #include "Track.h" +#define H264_TYPE(v) ((uint8_t)(v) & 0x1F) + namespace mediakit{ bool getAVCInfo(const string &strSps,int &iVideoWidth, int &iVideoHeight, float &iVideoFps); @@ -42,6 +44,13 @@ class H264Frame : public Frame { public: typedef std::shared_ptr Ptr; + typedef enum { + NAL_SPS = 7, + NAL_PPS = 8, + NAL_IDR = 5, + NAL_B_P = 1 + } NalType; + char *data() const override{ return (char *)buffer.data(); } @@ -64,7 +73,7 @@ public: } bool keyFrame() const override { - return type == 5; + return type == NAL_IDR; } public: uint16_t sequence; @@ -95,7 +104,7 @@ public: } bool keyFrame() const override { - return (buffer_ptr[iPrefixSize] & 0x1F) == 5; + return H264_TYPE(buffer_ptr[iPrefixSize]) == H264Frame::NAL_IDR; } }; @@ -193,9 +202,10 @@ public: * @param frame 数据帧 */ void inputFrame(const Frame::Ptr &frame) override{ - int type = (*((uint8_t *)frame->data() + frame->prefixSize())) & 0x1F; + int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); + switch (type){ - case 7:{ + case H264Frame::NAL_SPS:{ //sps bool flag = _sps.empty(); _sps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize()); @@ -204,19 +214,19 @@ public: } } break; - case 8:{ + case H264Frame::NAL_PPS:{ //pps _pps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize()); } break; - case 5:{ + case H264Frame::NAL_IDR:{ //I if(!_sps.empty()){ if(!_spsFrame) { H264Frame::Ptr insertFrame = std::make_shared(); - insertFrame->type = 7; + insertFrame->type = H264Frame::NAL_SPS; insertFrame->timeStamp = frame->stamp(); insertFrame->buffer.assign("\x0\x0\x0\x1",4); insertFrame->buffer.append(_sps); @@ -231,7 +241,7 @@ public: if(!_ppsFrame) { H264Frame::Ptr insertFrame = std::make_shared(); - insertFrame->type = 8; + insertFrame->type = H264Frame::NAL_PPS; insertFrame->timeStamp = frame->stamp(); insertFrame->buffer.assign("\x0\x0\x0\x1",4); insertFrame->buffer.append(_pps); @@ -245,7 +255,7 @@ public: } break; - case 1:{ + case H264Frame::NAL_B_P:{ //B or P VideoTrack::inputFrame(frame); } diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 8d0383ec..918ac597 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -1,29 +1,28 @@ /* - * MIT License - * - * Copyright (c) 2016 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +* MIT License +* +* Copyright (c) 2016 xiongziliang <771730766@qq.com> +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ #ifndef ZLMEDIAKIT_H265_H #define ZLMEDIAKIT_H265_H @@ -31,7 +30,9 @@ #include "Frame.h" #include "Track.h" -namespace mediakit{ +namespace mediakit { + +#define H265_TYPE(v) (((uint8_t)(v) >> 1) & 0x3f) /** * 265帧类 @@ -40,30 +41,76 @@ class H265Frame : public Frame { public: typedef std::shared_ptr Ptr; - char *data() const override{ - return (char *)buffer.data(); + typedef enum { + NAL_TRAIL_N = 0, + NAL_TRAIL_R = 1, + NAL_TSA_N = 2, + NAL_TSA_R = 3, + NAL_STSA_N = 4, + NAL_STSA_R = 5, + NAL_RADL_N = 6, + NAL_RADL_R = 7, + NAL_RASL_N = 8, + NAL_RASL_R = 9, + NAL_BLA_W_LP = 16, + NAL_BLA_W_RADL = 17, + NAL_BLA_N_LP = 18, + NAL_IDR_W_RADL = 19, + NAL_IDR_N_LP = 20, + NAL_CRA_NUT = 21, + NAL_VPS = 32, + NAL_SPS = 33, + NAL_PPS = 34, + NAL_AUD = 35, + NAL_EOS_NUT = 36, + NAL_EOB_NUT = 37, + NAL_FD_NUT = 38, + NAL_SEI_PREFIX = 39, + NAL_SEI_SUFFIX = 40, + } NaleType; + + char *data() const override { + return (char *) buffer.data(); } + uint32_t size() const override { return buffer.size(); } + uint32_t stamp() const override { return timeStamp; } - uint32_t prefixSize() const override{ + + uint32_t prefixSize() const override { return iPrefixSize; } - TrackType getTrackType() const override{ + TrackType getTrackType() const override { return TrackVideo; } - CodecId getCodecId() const override{ + CodecId getCodecId() const override { return CodecH265; } bool keyFrame() const override { - return type == 5; + return isKeyFrame(type); } + + static bool isKeyFrame(int type) { + switch (type) { + case NAL_BLA_N_LP: + case NAL_BLA_W_LP: + case NAL_BLA_W_RADL: + case NAL_CRA_NUT: + case NAL_IDR_N_LP: + case NAL_IDR_W_RADL: + return true; + default: + return false; + } + } + public: uint16_t sequence; uint32_t timeStamp; @@ -77,27 +124,222 @@ class H265FrameNoCopyAble : public FrameNoCopyAble { public: typedef std::shared_ptr Ptr; - H265FrameNoCopyAble(char *ptr,uint32_t size,uint32_t stamp,int prefixeSize = 4){ + H265FrameNoCopyAble(char *ptr, uint32_t size, uint32_t stamp, int prefixeSize = 4) { buffer_ptr = ptr; buffer_size = size; timeStamp = stamp; iPrefixSize = prefixeSize; } - TrackType getTrackType() const override{ + TrackType getTrackType() const override { return TrackVideo; } - CodecId getCodecId() const override{ + CodecId getCodecId() const override { return CodecH265; } bool keyFrame() const override { - return (buffer_ptr[iPrefixSize] & 0x1F) == 5; + int type = H265_TYPE(((uint8_t *) buffer_ptr)[iPrefixSize]); + return H265Frame::isKeyFrame(type); } }; +/** +* 265视频通道 +*/ +class H265Track : public VideoTrack { +public: + typedef std::shared_ptr Ptr; + + /** + * 不指定sps pps构造h265类型的媒体 + * 在随后的inputFrame中获取sps pps + */ + H265Track() {} + + /** + * 构造h265类型的媒体 + * @param sps sps帧数据 + * @param pps pps帧数据 + * @param sps_prefix_len 265头长度,可以为3个或4个字节,一般为0x00 00 00 01 + * @param pps_prefix_len 265头长度,可以为3个或4个字节,一般为0x00 00 00 01 + */ + H265Track(const string &sps, const string &pps, int sps_prefix_len = 4, int pps_prefix_len = 4) { + _sps = sps.substr(sps_prefix_len); + _pps = pps.substr(pps_prefix_len); + parseSps(_sps); + } + + /** + * 构造h265类型的媒体 + * @param sps sps帧 + * @param pps pps帧 + */ + H265Track(const Frame::Ptr &sps, const Frame::Ptr &pps) { + if (sps->getCodecId() != CodecH265 || pps->getCodecId() != CodecH265) { + throw std::invalid_argument("必须输入H265类型的帧"); + } + _sps = string(sps->data() + sps->prefixSize(), sps->size() - sps->prefixSize()); + _pps = string(pps->data() + pps->prefixSize(), pps->size() - pps->prefixSize()); + parseSps(_sps); + } + + /** + * 返回不带0x00 00 00 01头的sps + * @return + */ + const string &getSps() const { + return _sps; + } + + /** + * 返回不带0x00 00 00 01头的pps + * @return + */ + const string &getPps() const { + return _pps; + } + + CodecId getCodecId() const override { + return CodecH265; + } + + /** + * 返回视频高度 + * @return + */ + int getVideoHeight() const override { + return _width; + } + + /** + * 返回视频宽度 + * @return + */ + int getVideoWidth() const override { + return _height; + } + + /** + * 返回视频fps + * @return + */ + float getVideoFps() const override { + return _fps; + } + + bool ready() override { + return !_sps.empty() && !_pps.empty(); + } + + + /** + * 输入数据帧,并获取sps pps + * @param frame 数据帧 + */ + void inputFrame(const Frame::Ptr &frame) override { + int type = H265_TYPE(((uint8_t *) frame->data() + frame->prefixSize())[0]); + if (H265Frame::isKeyFrame(type)) { + //关键帧之前插入vps sps pps + if(!_vps.empty()){ + if (!_vpsFrame) { + H265Frame::Ptr insertFrame = std::make_shared(); + insertFrame->type = H265Frame::NAL_VPS; + insertFrame->timeStamp = frame->stamp(); + insertFrame->buffer.assign("\x0\x0\x0\x1", 4); + insertFrame->buffer.append(_sps); + insertFrame->iPrefixSize = 4; + _vpsFrame = insertFrame; + } + _vpsFrame->timeStamp = frame->stamp(); + VideoTrack::inputFrame(_vpsFrame); + } + if (!_sps.empty()) { + if (!_spsFrame) { + H265Frame::Ptr insertFrame = std::make_shared(); + insertFrame->type = H265Frame::NAL_SPS; + insertFrame->timeStamp = frame->stamp(); + insertFrame->buffer.assign("\x0\x0\x0\x1", 4); + insertFrame->buffer.append(_sps); + insertFrame->iPrefixSize = 4; + _spsFrame = insertFrame; + } + _spsFrame->timeStamp = frame->stamp(); + VideoTrack::inputFrame(_spsFrame); + } + + if (!_pps.empty()) { + if (!_ppsFrame) { + H265Frame::Ptr insertFrame = std::make_shared(); + insertFrame->type = H265Frame::NAL_PPS; + insertFrame->timeStamp = frame->stamp(); + insertFrame->buffer.assign("\x0\x0\x0\x1", 4); + insertFrame->buffer.append(_pps); + insertFrame->iPrefixSize = 4; + _ppsFrame = insertFrame; + } + _ppsFrame->timeStamp = frame->stamp(); + VideoTrack::inputFrame(_ppsFrame); + } + VideoTrack::inputFrame(frame); + return; + } + + //非idr帧 + switch (type) { + case H265Frame::NAL_VPS: { + //vps + _vps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + } + break; + + case H265Frame::NAL_SPS: { + //sps + _sps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + } + break; + case H265Frame::NAL_PPS: { + //pps + _pps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + } + break; + + default: { + //other frames + VideoTrack::inputFrame(frame); + } + break; + } + } +private: + /** + * 解析sps获取宽高fps + * @param sps sps不含头数据 + */ + void parseSps(const string &sps) { +// getAVCInfo(sps,_width,_height,_fps); + } + + Track::Ptr clone() override { + return std::make_shared::type>(*this); + } + +private: + string _vps; + string _sps; + string _pps; + int _width = 0; + int _height = 0; + float _fps = 0; + + H265Frame::Ptr _vpsFrame; + H265Frame::Ptr _spsFrame; + H265Frame::Ptr _ppsFrame; +}; + + }//namespace mediakit diff --git a/src/MediaFile/HLSMaker.cpp b/src/MediaFile/HLSMaker.cpp index 221ac9ac..177bfb0c 100644 --- a/src/MediaFile/HLSMaker.cpp +++ b/src/MediaFile/HLSMaker.cpp @@ -27,6 +27,7 @@ #include "HLSMaker.h" #include "Util/File.h" #include "Util/uv_errno.h" +#include "Extension/H264.h" using namespace toolkit; namespace mediakit { @@ -136,9 +137,10 @@ void HLSMaker::inputH264(void *data, uint32_t length, uint32_t timeStamp) { _ui32LastStamp = timeStamp; } int stampInc = timeStamp - _ui32LastStamp; - auto type = ((uint8_t*)data)[4] & 0x1F; + auto type = H264_TYPE(((uint8_t*)data)[4]); + switch (type) { - case 7: //SPS + case H264Frame::NAL_SPS: //SPS if (stampInc >= _ui32SegmentDuration * 1000) { _ui32LastStamp = timeStamp; //关闭文件 @@ -156,11 +158,11 @@ void HLSMaker::inputH264(void *data, uint32_t length, uint32_t timeStamp) { } write_index_file(_ui64TsCnt - _ui32NumSegments, _ui64TsCnt, 0); } - case 1: //P + case H264Frame::NAL_B_P: //P //insert aud frame before p and SPS frame _ts.inputH264("\x0\x0\x0\x1\x9\xf0", 6, timeStamp * 90); - case 5: //IDR - case 8: //PPS + case H264Frame::NAL_IDR: //IDR + case H264Frame::NAL_PPS: //PPS _ts.inputH264((char *) data, length, timeStamp * 90); break; default: diff --git a/src/MediaFile/Mp4Maker.cpp b/src/MediaFile/Mp4Maker.cpp index 110c7b07..82f4beb2 100644 --- a/src/MediaFile/Mp4Maker.cpp +++ b/src/MediaFile/Mp4Maker.cpp @@ -76,10 +76,10 @@ Mp4Maker::~Mp4Maker() { } void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp){ - auto iType = ((uint8_t*)pData)[0] & 0x1F; + auto iType = H264_TYPE(((uint8_t*)pData)[0]); switch (iType) { - case 1: //P - case 5: { //IDR + case H264Frame::NAL_B_P: //P + case H264Frame::NAL_IDR: { //IDR if (_strLastVideo.size()) { int64_t iTimeInc = (int64_t)ui32TimeStamp - (int64_t)_ui32LastVideoTime; iTimeInc = MAX(0,MIN(iTimeInc,500)); @@ -115,9 +115,8 @@ void Mp4Maker::inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp void Mp4Maker::inputH264_l(void *pData, uint32_t ui32Length, uint32_t ui32Duration) { GET_CONFIG_AND_REGISTER(uint32_t,recordSec,Record::kFileSecond); - - auto iType = ((uint8_t*)pData)[4] & 0x1F; - if(iType == 5 && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)){ + auto iType = H264_TYPE(((uint8_t*)pData)[4]); + if(iType == H264Frame::NAL_IDR && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)){ //在I帧率处新建MP4文件 //如果文件未创建或者文件超过10分钟则创建新文件 createFile(); diff --git a/src/RtmpMuxer/H264RtmpCodec.cpp b/src/RtmpMuxer/H264RtmpCodec.cpp index a48e6014..701c3e0c 100644 --- a/src/RtmpMuxer/H264RtmpCodec.cpp +++ b/src/RtmpMuxer/H264RtmpCodec.cpp @@ -74,24 +74,23 @@ bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) { inline void H264RtmpDecoder::onGetH264_l(const char* pcData, int iLen, uint32_t ui32TimeStamp) { - switch (pcData[0] & 0x1F) { - case 5: { + switch (H264_TYPE(pcData[0])) { + case H264Frame::NAL_IDR: { //I frame onGetH264(_sps.data(), _sps.length(), ui32TimeStamp); onGetH264(_pps.data(), _pps.length(), ui32TimeStamp); } - case 1: { + case H264Frame::NAL_B_P: { //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) { - _h264frame->type = pcData[0] & 0x1F; + _h264frame->type = H264_TYPE(pcData[0]); _h264frame->timeStamp = ui32TimeStamp; _h264frame->buffer.assign("\x0\x0\x0\x1", 4); //添加264头 _h264frame->buffer.append(pcData, iLen); @@ -114,12 +113,12 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { auto pcData = frame->data() + frame->prefixSize(); auto iLen = frame->size() - frame->prefixSize(); - auto type = ((uint8_t*)pcData)[0] & 0x1F; + auto type = H264_TYPE(((uint8_t*)pcData)[0]); if(!_gotSpsPps){ //尝试从frame中获取sps pps switch (type){ - case 7:{ + case H264Frame::NAL_SPS:{ //sps if(_sps.empty()){ _sps = string(pcData,iLen); @@ -129,7 +128,7 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { } } break; - case 8:{ + case H264Frame::NAL_PPS:{ //pps if(_pps.empty()){ _pps = string(pcData,iLen); @@ -152,8 +151,8 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { } switch (type){ - case 5: - case 1:{ + case H264Frame::NAL_IDR: + case H264Frame::NAL_B_P:{ //I or P or B frame int8_t flags = 7; //h.264 bool is_config = false; diff --git a/src/RtspMuxer/H264RtpCodec.cpp b/src/RtspMuxer/H264RtpCodec.cpp index 4510bd64..fab32cf5 100644 --- a/src/RtspMuxer/H264RtpCodec.cpp +++ b/src/RtspMuxer/H264RtpCodec.cpp @@ -103,7 +103,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { _h264frame->type = nal.type; _h264frame->timeStamp = rtppack->timeStamp; _h264frame->sequence = rtppack->sequence; - auto isIDR = _h264frame->type == 5; + auto isIDR = _h264frame->type == H264Frame::NAL_IDR; onGetH264(_h264frame); return (isIDR); //i frame } @@ -121,7 +121,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { _h264frame->type = fu.type; _h264frame->timeStamp = rtppack->timeStamp; _h264frame->sequence = rtppack->sequence; - return (_h264frame->type == 5); //i frame + return (_h264frame->type == H264Frame::NAL_IDR); //i frame } if (rtppack->sequence != (uint16_t)(_h264frame->sequence + 1)) { @@ -134,7 +134,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { //FU-A end _h264frame->buffer.append((char *)frame + 2, length - 2); _h264frame->timeStamp = rtppack->timeStamp; - auto isIDR = _h264frame->type == 5; + auto isIDR = _h264frame->type == H264Frame::NAL_IDR; onGetH264(_h264frame); return isIDR; } @@ -256,8 +256,8 @@ void H264RtpEncoder::makeH264Rtp(const void* data, unsigned int len, bool mark, rtppkt->type = TrackVideo; rtppkt->offset = 16; - uint8_t type = ((uint8_t *) (data))[0] & 0x1F; - RtpCodec::inputRtp(rtppkt,type == 7); + uint8_t type = H264_TYPE(((uint8_t *) (data))[0]); + RtpCodec::inputRtp(rtppkt,type == H264Frame::NAL_IDR); _ui16Sequence++; _ui32TimeStamp = uiStamp; } diff --git a/src/RtspMuxer/H265RtpCodec.cpp b/src/RtspMuxer/H265RtpCodec.cpp index d59f626f..b469506e 100644 --- a/src/RtspMuxer/H265RtpCodec.cpp +++ b/src/RtspMuxer/H265RtpCodec.cpp @@ -256,8 +256,8 @@ void H265RtpEncoder::makeH265Rtp(const void* data, unsigned int len, bool mark, rtppkt->type = TrackVideo; rtppkt->offset = 16; - uint8_t type = ((uint8_t *) (data))[0] & 0x1F; - RtpCodec::inputRtp(rtppkt,type == 7); + uint8_t type = H265_TYPE(((uint8_t *) (data))[0]); + RtpCodec::inputRtp(rtppkt,type == H265Frame::isKeyFrame(type)); _ui16Sequence++; _ui32TimeStamp = uiStamp; }