mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 12:37:09 +08:00
整理H264/H265 Frame对象及相关代码
This commit is contained in:
parent
4fb4471647
commit
35d9321b93
@ -58,12 +58,12 @@ public:
|
|||||||
typedef std::shared_ptr<FrameCacheAble> Ptr;
|
typedef std::shared_ptr<FrameCacheAble> Ptr;
|
||||||
|
|
||||||
FrameCacheAble(const Frame::Ptr &frame){
|
FrameCacheAble(const Frame::Ptr &frame){
|
||||||
if(frame->cacheAble()){
|
if (frame->cacheAble()) {
|
||||||
_frame = frame;
|
_frame = frame;
|
||||||
_ptr = frame->data();
|
_ptr = frame->data();
|
||||||
}else{
|
} else {
|
||||||
_buffer = FrameImp::create();
|
_buffer = FrameImp::create();
|
||||||
_buffer->_buffer.assign(frame->data(),frame->size());
|
_buffer->_buffer.assign(frame->data(), frame->size());
|
||||||
_ptr = _buffer->data();
|
_ptr = _buffer->data();
|
||||||
}
|
}
|
||||||
_size = frame->size();
|
_size = frame->size();
|
||||||
@ -73,6 +73,8 @@ public:
|
|||||||
_codec_id = frame->getCodecId();
|
_codec_id = frame->getCodecId();
|
||||||
_key = frame->keyFrame();
|
_key = frame->keyFrame();
|
||||||
_config = frame->configFrame();
|
_config = frame->configFrame();
|
||||||
|
_drop_able = frame->dropAble();
|
||||||
|
_decode_able = frame->decodeAble();
|
||||||
}
|
}
|
||||||
|
|
||||||
~FrameCacheAble() override = default;
|
~FrameCacheAble() override = default;
|
||||||
@ -92,9 +94,19 @@ public:
|
|||||||
return _config;
|
return _config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dropAble() const override {
|
||||||
|
return _drop_able;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decodeAble() const override {
|
||||||
|
return _decode_able;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _key;
|
bool _key;
|
||||||
bool _config;
|
bool _config;
|
||||||
|
bool _drop_able;
|
||||||
|
bool _decode_able;
|
||||||
Frame::Ptr _frame;
|
Frame::Ptr _frame;
|
||||||
FrameImp::Ptr _buffer;
|
FrameImp::Ptr _buffer;
|
||||||
};
|
};
|
||||||
@ -153,18 +165,18 @@ const char* getTrackString(TrackType type){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *CodecInfo::getCodecName() {
|
const char *CodecInfo::getCodecName() const {
|
||||||
return mediakit::getCodecName(getCodecId());
|
return mediakit::getCodecName(getCodecId());
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackType CodecInfo::getTrackType() {
|
TrackType CodecInfo::getTrackType() const {
|
||||||
return mediakit::getTrackType(getCodecId());
|
return mediakit::getTrackType(getCodecId());
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t constexpr kMaxFrameCacheSize = 100;
|
static size_t constexpr kMaxFrameCacheSize = 100;
|
||||||
|
|
||||||
bool FrameMerger::willFlush(const Frame::Ptr &frame) const{
|
bool FrameMerger::willFlush(const Frame::Ptr &frame) const{
|
||||||
if (_frameCached.empty()) {
|
if (_frame_cache.empty()) {
|
||||||
//缓存为空
|
//缓存为空
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -182,36 +194,20 @@ bool FrameMerger::willFlush(const Frame::Ptr &frame) const{
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
//遇到新帧、或时间戳变化或缓存太多,防止内存溢出,则flush输出
|
//遇到新帧、或时间戳变化或缓存太多,防止内存溢出,则flush输出
|
||||||
return new_frame || _frameCached.back()->dts() != frame->dts() || _frameCached.size() > kMaxFrameCacheSize;
|
return new_frame || _frame_cache.back()->dts() != frame->dts() || _frame_cache.size() > kMaxFrameCacheSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
case mp4_nal_size:
|
case mp4_nal_size:
|
||||||
case h264_prefix: {
|
case h264_prefix: {
|
||||||
if (!frameCacheHasVCL()) {
|
if (!_have_decode_able_frame) {
|
||||||
//缓存中没有有效的能解码的帧,所以这次不flush
|
//缓存中没有有效的能解码的帧,所以这次不flush
|
||||||
return false;
|
return _frame_cache.size() > kMaxFrameCacheSize;
|
||||||
}
|
}
|
||||||
if (_frameCached.back()->dts() != frame->dts()) {
|
if (_frame_cache.back()->dts() != frame->dts() || frame->decodeAble()) {
|
||||||
//时间戳变化了,立即flush
|
//时间戳变化了,或新的一帧,立即flush
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
switch (frame->getCodecId()) {
|
return _frame_cache.size() > kMaxFrameCacheSize;
|
||||||
case CodecH264 : {
|
|
||||||
auto type = H264_TYPE(frame->data()[frame->prefixSize()]);
|
|
||||||
// sei aud pps sps 不判断;264 新一帧的开始,刷新输出
|
|
||||||
return (frame->data()[frame->prefixSize() + 1] & 0x80) != 0 && type >= H264Frame::NAL_B_P &&
|
|
||||||
type <= H264Frame::NAL_IDR;
|
|
||||||
}
|
|
||||||
case CodecH265 : {
|
|
||||||
auto type = H265_TYPE(frame->data()[frame->prefixSize()]);
|
|
||||||
//first_slice_segment_in_pic_flag is frame start
|
|
||||||
return (type >= H265Frame::NAL_TRAIL_R && type <= H265Frame::NAL_RSV_IRAP_VCL23) &&
|
|
||||||
((frame->data()[frame->prefixSize() + 2] >> 7 & 0x01) != 0);
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
//缓存太多,防止内存溢出
|
|
||||||
return _frameCached.size() > kMaxFrameCacheSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default: /*不可达*/ assert(0); return true;
|
default: /*不可达*/ assert(0); return true;
|
||||||
}
|
}
|
||||||
@ -245,86 +241,39 @@ void FrameMerger::doMerge(BufferLikeString &merged, const Frame::Ptr &frame) con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FrameMerger::shouldDrop(const Frame::Ptr &frame) const{
|
void FrameMerger::inputFrame(const Frame::Ptr &frame, const onOutput &cb, BufferLikeString *buffer) {
|
||||||
switch (frame->getCodecId()) {
|
|
||||||
case CodecH264: {
|
|
||||||
switch (H264_TYPE(frame->data()[frame->prefixSize()])) {
|
|
||||||
// 防止把AUD或者SEI当成一帧
|
|
||||||
case H264Frame::NAL_SEI:
|
|
||||||
case H264Frame::NAL_AUD: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case CodecH265: {
|
|
||||||
switch (H265_TYPE(frame->data()[frame->prefixSize()])) {
|
|
||||||
// 防止把AUD或者SEI当成一帧
|
|
||||||
case H265Frame::NAL_AUD:
|
|
||||||
case H265Frame::NAL_SEI_SUFFIX:
|
|
||||||
case H265Frame::NAL_SEI_PREFIX: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FrameMerger::frameCacheHasVCL() const {
|
|
||||||
bool has_vcl = false;
|
|
||||||
bool is_h264_or_h265 = false;
|
|
||||||
_frameCached.for_each([&](const Frame::Ptr &frame) {
|
|
||||||
switch (frame->getCodecId()) {
|
|
||||||
case CodecH264: {
|
|
||||||
auto type = H264_TYPE(frame->data()[frame->prefixSize()]);
|
|
||||||
//有编码数据
|
|
||||||
has_vcl = type >= H264Frame::NAL_B_P && type <= H264Frame::NAL_IDR;
|
|
||||||
is_h264_or_h265 = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CodecH265: {
|
|
||||||
auto type = H265_TYPE(frame->data()[frame->prefixSize()]);
|
|
||||||
//有编码数据
|
|
||||||
has_vcl = type >= H265Frame::NAL_TRAIL_R && type <= H265Frame::NAL_RSV_IRAP_VCL23;
|
|
||||||
is_h264_or_h265 = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (is_h264_or_h265) {
|
|
||||||
return has_vcl;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrameMerger::inputFrame(const Frame::Ptr &frame, const onOutput &cb) {
|
|
||||||
if (willFlush(frame)) {
|
if (willFlush(frame)) {
|
||||||
Frame::Ptr back = _frameCached.back();
|
Frame::Ptr back = _frame_cache.back();
|
||||||
Buffer::Ptr merged_frame = back;
|
Buffer::Ptr merged_frame = back;
|
||||||
bool have_idr = back->keyFrame();
|
bool have_key_frame = back->keyFrame();
|
||||||
|
|
||||||
if (_frameCached.size() != 1 || _type == mp4_nal_size) {
|
if (_frame_cache.size() != 1 || _type == mp4_nal_size || buffer) {
|
||||||
//在MP4模式下,一帧数据也需要在前添加nalu_size
|
//在MP4模式下,一帧数据也需要在前添加nalu_size
|
||||||
BufferLikeString merged;
|
BufferLikeString tmp;
|
||||||
merged.reserve(back->size() + 1024);
|
BufferLikeString &merged = buffer ? *buffer : tmp;
|
||||||
_frameCached.for_each([&](const Frame::Ptr &frame) {
|
|
||||||
|
if (!buffer) {
|
||||||
|
tmp.reserve(back->size() + 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
_frame_cache.for_each([&](const Frame::Ptr &frame) {
|
||||||
doMerge(merged, frame);
|
doMerge(merged, frame);
|
||||||
if (frame->keyFrame()) {
|
if (frame->keyFrame()) {
|
||||||
have_idr = true;
|
have_key_frame = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
merged_frame = std::make_shared<BufferOffset<BufferLikeString> >(std::move(merged));
|
merged_frame = std::make_shared<BufferOffset<BufferLikeString> >(buffer ? merged : std::move(merged));
|
||||||
}
|
}
|
||||||
cb(back->dts(), back->pts(), merged_frame, have_idr);
|
cb(back->dts(), back->pts(), merged_frame, have_key_frame);
|
||||||
_frameCached.clear();
|
_frame_cache.clear();
|
||||||
|
_have_decode_able_frame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case h264_prefix:
|
case h264_prefix:
|
||||||
case mp4_nal_size: {
|
case mp4_nal_size: {
|
||||||
//h264头和mp4头模式过滤无效的帧
|
if (frame->dropAble()) {
|
||||||
if (shouldDrop(frame)) {
|
//h264头和mp4头模式过滤无效的帧
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -332,7 +281,10 @@ void FrameMerger::inputFrame(const Frame::Ptr &frame, const onOutput &cb) {
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
|
if (frame->decodeAble()) {
|
||||||
|
_have_decode_able_frame = true;
|
||||||
|
}
|
||||||
|
_frame_cache.emplace_back(Frame::getCacheAbleFrame(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameMerger::FrameMerger(int type) {
|
FrameMerger::FrameMerger(int type) {
|
||||||
@ -340,7 +292,8 @@ FrameMerger::FrameMerger(int type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FrameMerger::clear() {
|
void FrameMerger::clear() {
|
||||||
_frameCached.clear();
|
_frame_cache.clear();
|
||||||
|
_have_decode_able_frame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
@ -95,12 +95,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
* 获取编码器名称
|
* 获取编码器名称
|
||||||
*/
|
*/
|
||||||
const char *getCodecName();
|
const char *getCodecName() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取音视频类型
|
* 获取音视频类型
|
||||||
*/
|
*/
|
||||||
TrackType getTrackType();
|
TrackType getTrackType() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,6 +144,26 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool cacheAble() const { return true; }
|
virtual bool cacheAble() const { return true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该帧是否可以丢弃
|
||||||
|
* SEI/AUD帧可以丢弃
|
||||||
|
* 默认都不能丢帧
|
||||||
|
*/
|
||||||
|
virtual bool dropAble() const { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为可解码帧
|
||||||
|
* sps pps等帧不能解码
|
||||||
|
*/
|
||||||
|
virtual bool decodeAble() const {
|
||||||
|
if (getTrackType() != TrackVideo) {
|
||||||
|
//非视频帧都可以解码
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//默认非sps pps帧都可以解码
|
||||||
|
return !configFrame();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回可缓存的frame
|
* 返回可缓存的frame
|
||||||
*/
|
*/
|
||||||
@ -459,7 +479,7 @@ private:
|
|||||||
*/
|
*/
|
||||||
class FrameMerger {
|
class FrameMerger {
|
||||||
public:
|
public:
|
||||||
using onOutput = function<void(uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer, bool have_idr)>;
|
using onOutput = function<void(uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer, bool have_key_frame)>;
|
||||||
using Ptr = std::shared_ptr<FrameMerger>;
|
using Ptr = std::shared_ptr<FrameMerger>;
|
||||||
enum {
|
enum {
|
||||||
none = 0,
|
none = 0,
|
||||||
@ -471,17 +491,16 @@ public:
|
|||||||
~FrameMerger() = default;
|
~FrameMerger() = default;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
void inputFrame(const Frame::Ptr &frame, const onOutput &cb);
|
void inputFrame(const Frame::Ptr &frame, const onOutput &cb, BufferLikeString *buffer = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool willFlush(const Frame::Ptr &frame) const;
|
bool willFlush(const Frame::Ptr &frame) const;
|
||||||
void doMerge(BufferLikeString &buffer, const Frame::Ptr &frame) const;
|
void doMerge(BufferLikeString &buffer, const Frame::Ptr &frame) const;
|
||||||
bool shouldDrop(const Frame::Ptr &frame) const;
|
|
||||||
bool frameCacheHasVCL() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _type;
|
int _type;
|
||||||
List<Frame::Ptr> _frameCached;
|
bool _have_decode_able_frame = false;
|
||||||
|
List<Frame::Ptr> _frame_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
@ -276,48 +276,6 @@ Sdp::Ptr H264Track::getSdp() {
|
|||||||
return std::make_shared<H264Sdp>(getSps(), getPps(), getBitRate() / 1024);
|
return std::make_shared<H264Sdp>(getSps(), getPps(), getBitRate() / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool H264Frame::keyFrame() const {
|
|
||||||
//多slice 一帧的情况下检查 first_mb_in_slice 是否为0 表示其为一帧的开始
|
|
||||||
return H264_TYPE(_buffer[_prefix_size]) == H264Frame::NAL_IDR && (_buffer[_prefix_size + 1] & 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H264Frame::configFrame() const {
|
|
||||||
switch (H264_TYPE(_buffer[_prefix_size])) {
|
|
||||||
case H264Frame::NAL_SPS:
|
|
||||||
case H264Frame::NAL_PPS: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
H264Frame::H264Frame() {
|
|
||||||
_codec_id = CodecH264;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
H264FrameNoCacheAble::H264FrameNoCacheAble(char *ptr,size_t size,uint32_t dts , uint32_t pts ,size_t prefix_size){
|
|
||||||
_ptr = ptr;
|
|
||||||
_size = size;
|
|
||||||
_dts = dts;
|
|
||||||
_pts = pts;
|
|
||||||
_prefix_size = prefix_size;
|
|
||||||
_codec_id = CodecH264;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H264FrameNoCacheAble::keyFrame() const {
|
|
||||||
return H264_TYPE(_ptr[_prefix_size]) == H264Frame::NAL_IDR;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H264FrameNoCacheAble::configFrame() const {
|
|
||||||
switch (H264_TYPE(_ptr[_prefix_size])) {
|
|
||||||
case H264Frame::NAL_SPS:
|
|
||||||
case H264Frame::NAL_PPS: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@ bool getAVCInfo(const string &strSps,int &iVideoWidth, int &iVideoHeight, float
|
|||||||
void splitH264(const char *ptr, size_t len, size_t prefix, const std::function<void(const char *, size_t, size_t)> &cb);
|
void splitH264(const char *ptr, size_t len, size_t prefix, const std::function<void(const char *, size_t, size_t)> &cb);
|
||||||
size_t prefixSize(const char *ptr, size_t len);
|
size_t prefixSize(const char *ptr, size_t len);
|
||||||
|
|
||||||
/**
|
template<typename Parent>
|
||||||
* 264帧类
|
class H264FrameHelper : public Parent{
|
||||||
*/
|
|
||||||
class H264Frame : public FrameImp {
|
|
||||||
public:
|
public:
|
||||||
using Ptr = std::shared_ptr<H264Frame>;
|
friend class FrameImp;
|
||||||
|
friend class ResourcePool_l<H264FrameHelper>;
|
||||||
|
using Ptr = std::shared_ptr<H264FrameHelper>;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NAL_IDR = 5,
|
NAL_IDR = 5,
|
||||||
@ -39,28 +39,54 @@ public:
|
|||||||
NAL_B_P = 1,
|
NAL_B_P = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool keyFrame() const override;
|
template<typename ...ARGS>
|
||||||
bool configFrame() const override;
|
H264FrameHelper(ARGS &&...args): Parent(std::forward<ARGS>(args)...) {
|
||||||
|
this->_codec_id = CodecH264;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
~H264FrameHelper() override = default;
|
||||||
friend class FrameImp;
|
|
||||||
friend class ResourcePool_l<H264Frame>;
|
bool keyFrame() const override {
|
||||||
H264Frame();
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
return H264_TYPE(*nal_ptr) == NAL_IDR && decodeAble();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configFrame() const override {
|
||||||
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
switch (H264_TYPE(*nal_ptr)) {
|
||||||
|
case NAL_SPS:
|
||||||
|
case NAL_PPS: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dropAble() const override {
|
||||||
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
switch (H264_TYPE(*nal_ptr)) {
|
||||||
|
case NAL_SEI:
|
||||||
|
case NAL_AUD: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decodeAble() const override {
|
||||||
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
auto type = H264_TYPE(*nal_ptr);
|
||||||
|
//多slice情况下, first_mb_in_slice 表示其为一帧的开始
|
||||||
|
return type >= NAL_B_P && type <= NAL_IDR && (nal_ptr[1] & 0x80);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 264帧类
|
||||||
|
*/
|
||||||
|
using H264Frame = H264FrameHelper<FrameImp>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 防止内存拷贝的H264类
|
* 防止内存拷贝的H264类
|
||||||
* 用户可以通过该类型快速把一个指针无拷贝的包装成Frame类
|
* 用户可以通过该类型快速把一个指针无拷贝的包装成Frame类
|
||||||
* 该类型在DevChannel中有使用
|
|
||||||
*/
|
*/
|
||||||
class H264FrameNoCacheAble : public FrameFromPtr {
|
using H264FrameNoCacheAble = H264FrameHelper<FrameFromPtr>;
|
||||||
public:
|
|
||||||
using Ptr = std::shared_ptr<H264FrameNoCacheAble>;
|
|
||||||
|
|
||||||
H264FrameNoCacheAble(char *ptr,size_t size,uint32_t dts , uint32_t pts ,size_t prefix_size = 4);
|
|
||||||
bool keyFrame() const override;
|
|
||||||
bool configFrame() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 264视频通道
|
* 264视频通道
|
||||||
|
@ -157,10 +157,8 @@ void H264RtmpEncoder::makeConfigPacket(){
|
|||||||
void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
||||||
auto data = frame->data() + frame->prefixSize();
|
auto data = frame->data() + frame->prefixSize();
|
||||||
auto len = frame->size() - frame->prefixSize();
|
auto len = frame->size() - frame->prefixSize();
|
||||||
auto type = H264_TYPE(((uint8_t*)data)[0]);
|
auto type = H264_TYPE(data[0]);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case H264Frame::NAL_SEI:
|
|
||||||
case H264Frame::NAL_AUD: return;
|
|
||||||
case H264Frame::NAL_SPS: {
|
case H264Frame::NAL_SPS: {
|
||||||
if (!_got_config_frame) {
|
if (!_got_config_frame) {
|
||||||
_sps = string(data, len);
|
_sps = string(data, len);
|
||||||
@ -178,21 +176,6 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
|||||||
default : break;
|
default : break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame->configFrame() && _rtmp_packet && _has_vcl) {
|
|
||||||
//sps pps flush frame
|
|
||||||
RtmpCodec::inputRtmp(_rtmp_packet);
|
|
||||||
_has_vcl = false;
|
|
||||||
_rtmp_packet = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_rtmp_packet && (_rtmp_packet->time_stamp != frame->dts() || ((data[1] & 0x80) != 0 && type >= H264Frame::NAL_B_P && type <= H264Frame::NAL_IDR && _has_vcl))) {
|
|
||||||
RtmpCodec::inputRtmp(_rtmp_packet);
|
|
||||||
_has_vcl = false;
|
|
||||||
_rtmp_packet = nullptr;
|
|
||||||
}
|
|
||||||
if (type >= H264Frame::NAL_B_P && type <= H264Frame::NAL_IDR) {
|
|
||||||
_has_vcl = true;
|
|
||||||
}
|
|
||||||
if (!_rtmp_packet) {
|
if (!_rtmp_packet) {
|
||||||
//I or P or B frame
|
//I or P or B frame
|
||||||
int8_t flags = FLV_CODEC_H264;
|
int8_t flags = FLV_CODEC_H264;
|
||||||
@ -200,6 +183,7 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
|||||||
flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
|
flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
|
||||||
|
|
||||||
_rtmp_packet = RtmpPacket::create();
|
_rtmp_packet = RtmpPacket::create();
|
||||||
|
_rtmp_packet->buffer.clear();
|
||||||
_rtmp_packet->buffer.push_back(flags);
|
_rtmp_packet->buffer.push_back(flags);
|
||||||
_rtmp_packet->buffer.push_back(!is_config);
|
_rtmp_packet->buffer.push_back(!is_config);
|
||||||
int32_t cts = frame->pts() - frame->dts();
|
int32_t cts = frame->pts() - frame->dts();
|
||||||
@ -213,10 +197,13 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
|||||||
_rtmp_packet->time_stamp = frame->dts();
|
_rtmp_packet->time_stamp = frame->dts();
|
||||||
_rtmp_packet->type_id = MSG_VIDEO;
|
_rtmp_packet->type_id = MSG_VIDEO;
|
||||||
}
|
}
|
||||||
uint32_t size = htonl((uint32_t) len);
|
|
||||||
_rtmp_packet->buffer.append((char *) &size, 4);
|
_merger.inputFrame(frame, [&](uint32_t, uint32_t, const Buffer::Ptr &, bool) {
|
||||||
_rtmp_packet->buffer.append(data, len);
|
//输出rtmp packet
|
||||||
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
|
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
|
||||||
|
RtmpCodec::inputRtmp(_rtmp_packet);
|
||||||
|
_rtmp_packet = nullptr;
|
||||||
|
}, &_rtmp_packet->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void H264RtmpEncoder::makeVideoConfigPkt() {
|
void H264RtmpEncoder::makeVideoConfigPkt() {
|
||||||
|
@ -75,13 +75,15 @@ public:
|
|||||||
* 生成config包
|
* 生成config包
|
||||||
*/
|
*/
|
||||||
void makeConfigPacket() override;
|
void makeConfigPacket() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void makeVideoConfigPkt();
|
void makeVideoConfigPkt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _has_vcl = false;
|
|
||||||
bool _got_config_frame = false;
|
bool _got_config_frame = false;
|
||||||
H264Track::Ptr _track;
|
H264Track::Ptr _track;
|
||||||
RtmpPacket::Ptr _rtmp_packet;
|
RtmpPacket::Ptr _rtmp_packet;
|
||||||
|
FrameMerger _merger{FrameMerger::mp4_nal_size};
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
@ -50,56 +50,6 @@ bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, i
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool H265Frame::keyFrame() const {
|
|
||||||
return isKeyFrame(H265_TYPE(_buffer[_prefix_size]), _buffer.data() + _prefix_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H265Frame::configFrame() const {
|
|
||||||
switch (H265_TYPE(_buffer[_prefix_size])) {
|
|
||||||
case H265Frame::NAL_VPS:
|
|
||||||
case H265Frame::NAL_SPS:
|
|
||||||
case H265Frame::NAL_PPS : return true;
|
|
||||||
default : return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H265Frame::isKeyFrame(int type, const char *ptr) {
|
|
||||||
if (ptr) {
|
|
||||||
return (((*((uint8_t *) ptr + 2)) >> 7) & 0x01) == 1 && (type == NAL_IDR_N_LP || type == NAL_IDR_W_RADL);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
H265Frame::H265Frame(){
|
|
||||||
_codec_id = CodecH265;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
H265FrameNoCacheAble::H265FrameNoCacheAble(char *ptr, size_t size, uint32_t dts, uint32_t pts, size_t prefix_size) {
|
|
||||||
_ptr = ptr;
|
|
||||||
_size = size;
|
|
||||||
_dts = dts;
|
|
||||||
_pts = pts;
|
|
||||||
_prefix_size = prefix_size;
|
|
||||||
_codec_id = CodecH265;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H265FrameNoCacheAble::keyFrame() const {
|
|
||||||
return H265Frame::isKeyFrame(H265_TYPE(((uint8_t *) _ptr)[_prefix_size]), _ptr + _prefix_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool H265FrameNoCacheAble::configFrame() const {
|
|
||||||
switch (H265_TYPE(((uint8_t *) _ptr)[_prefix_size])) {
|
|
||||||
case H265Frame::NAL_VPS:
|
|
||||||
case H265Frame::NAL_SPS:
|
|
||||||
case H265Frame::NAL_PPS: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
H265Track::H265Track(const string &vps,const string &sps, const string &pps,int vps_prefix_len, int sps_prefix_len, int pps_prefix_len) {
|
H265Track::H265Track(const string &vps,const string &sps, const string &pps,int vps_prefix_len, int sps_prefix_len, int pps_prefix_len) {
|
||||||
_vps = vps.substr(vps_prefix_len);
|
_vps = vps.substr(vps_prefix_len);
|
||||||
_sps = sps.substr(sps_prefix_len);
|
_sps = sps.substr(sps_prefix_len);
|
||||||
@ -154,17 +104,16 @@ void H265Track::inputFrame(const Frame::Ptr &frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void H265Track::inputFrame_l(const Frame::Ptr &frame) {
|
void H265Track::inputFrame_l(const Frame::Ptr &frame) {
|
||||||
int type = H265_TYPE(((uint8_t *) frame->data() + frame->prefixSize())[0]);
|
if (frame->keyFrame()) {
|
||||||
if (H265Frame::isKeyFrame(type, frame->data() + frame->prefixSize())) {
|
|
||||||
insertConfigFrame(frame);
|
insertConfigFrame(frame);
|
||||||
VideoTrack::inputFrame(frame);
|
VideoTrack::inputFrame(frame);
|
||||||
_is_idr = true;
|
_is_idr = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_is_idr = false;
|
|
||||||
|
|
||||||
|
_is_idr = false;
|
||||||
//非idr帧
|
//非idr帧
|
||||||
switch (type) {
|
switch (H265_TYPE(((uint8_t *) frame->data() + frame->prefixSize())[0])) {
|
||||||
case H265Frame::NAL_VPS: {
|
case H265Frame::NAL_VPS: {
|
||||||
_vps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
_vps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
||||||
break;
|
break;
|
||||||
|
@ -22,12 +22,13 @@ namespace mediakit {
|
|||||||
|
|
||||||
bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, int &iVideoHeight, float &iVideoFps);
|
bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, int &iVideoHeight, float &iVideoFps);
|
||||||
|
|
||||||
/**
|
template<typename Parent>
|
||||||
* 265帧类
|
class H265FrameHelper : public Parent{
|
||||||
*/
|
|
||||||
class H265Frame : public FrameImp {
|
|
||||||
public:
|
public:
|
||||||
using Ptr = std::shared_ptr<H265Frame>;
|
friend class FrameImp;
|
||||||
|
friend class ResourcePool_l<H265FrameHelper>;
|
||||||
|
using Ptr = std::shared_ptr<H265FrameHelper>;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NAL_TRAIL_N = 0,
|
NAL_TRAIL_N = 0,
|
||||||
NAL_TRAIL_R = 1,
|
NAL_TRAIL_R = 1,
|
||||||
@ -59,24 +60,57 @@ public:
|
|||||||
NAL_SEI_SUFFIX = 40,
|
NAL_SEI_SUFFIX = 40,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool keyFrame() const override;
|
template<typename ...ARGS>
|
||||||
bool configFrame() const override;
|
H265FrameHelper(ARGS &&...args): Parent(std::forward<ARGS>(args)...) {
|
||||||
static bool isKeyFrame(int type, const char* ptr);
|
this->_codec_id = CodecH265;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
~H265FrameHelper() override = default;
|
||||||
friend class FrameImp;
|
|
||||||
friend class ResourcePool_l<H265Frame>;
|
bool keyFrame() const override {
|
||||||
H265Frame();
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
auto type = H265_TYPE(*nal_ptr);
|
||||||
|
return (type == NAL_IDR_N_LP || type == NAL_IDR_W_RADL) && decodeAble();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configFrame() const override {
|
||||||
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
switch (H265_TYPE(*nal_ptr)) {
|
||||||
|
case NAL_VPS:
|
||||||
|
case NAL_SPS:
|
||||||
|
case NAL_PPS : return true;
|
||||||
|
default : return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dropAble() const override {
|
||||||
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
switch (H264_TYPE(*nal_ptr)) {
|
||||||
|
case NAL_AUD:
|
||||||
|
case NAL_SEI_SUFFIX:
|
||||||
|
case NAL_SEI_PREFIX: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decodeAble() const override {
|
||||||
|
auto nal_ptr = (uint8_t *) this->data() + this->prefixSize();
|
||||||
|
auto type = H265_TYPE(*nal_ptr);
|
||||||
|
//多slice情况下, first_slice_segment_in_pic_flag 表示其为一帧的开始
|
||||||
|
return type >= NAL_TRAIL_R && type <= NAL_RSV_IRAP_VCL23 && (nal_ptr[2] & 0x80);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class H265FrameNoCacheAble : public FrameFromPtr {
|
/**
|
||||||
public:
|
* 265帧类
|
||||||
using Ptr = std::shared_ptr<H265FrameNoCacheAble>;
|
*/
|
||||||
|
using H265Frame = H265FrameHelper<FrameImp>;
|
||||||
|
|
||||||
H265FrameNoCacheAble(char *ptr, size_t size, uint32_t dts,uint32_t pts, size_t prefix_size = 4);
|
/**
|
||||||
bool keyFrame() const override;
|
* 防止内存拷贝的H265类
|
||||||
bool configFrame() const override;
|
* 用户可以通过该类型快速把一个指针无拷贝的包装成Frame类
|
||||||
};
|
*/
|
||||||
|
using H265FrameNoCacheAble = H265FrameHelper<FrameFromPtr>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 265视频通道
|
* 265视频通道
|
||||||
|
@ -136,8 +136,7 @@ void H265RtmpEncoder::makeConfigPacket(){
|
|||||||
void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
||||||
auto data = frame->data() + frame->prefixSize();
|
auto data = frame->data() + frame->prefixSize();
|
||||||
auto len = frame->size() - frame->prefixSize();
|
auto len = frame->size() - frame->prefixSize();
|
||||||
auto type = H265_TYPE(((uint8_t*)data)[0]);
|
auto type = H265_TYPE(data[0]);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case H265Frame::NAL_SPS: {
|
case H265Frame::NAL_SPS: {
|
||||||
if (!_got_config_frame) {
|
if (!_got_config_frame) {
|
||||||
@ -160,37 +159,16 @@ void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case H265Frame::NAL_AUD:
|
|
||||||
case H265Frame::NAL_SEI_PREFIX:
|
|
||||||
case H265Frame::NAL_SEI_SUFFIX: return;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(frame->configFrame() && _rtmp_packet && _has_vcl){
|
|
||||||
// sps pps flush frame
|
|
||||||
RtmpCodec::inputRtmp(_rtmp_packet);
|
|
||||||
_rtmp_packet = nullptr;
|
|
||||||
_has_vcl = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_rtmp_packet && (_rtmp_packet->time_stamp != frame->dts() || (_has_vcl && type >= H265Frame::NAL_TRAIL_R && type <= H265Frame::NAL_RSV_IRAP_VCL23 && (data[2] >> 7 & 0x01) != 0))) {
|
|
||||||
RtmpCodec::inputRtmp(_rtmp_packet);
|
|
||||||
_has_vcl = false;
|
|
||||||
_rtmp_packet = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type >= H265Frame::NAL_TRAIL_R && type <= H265Frame::NAL_RSV_IRAP_VCL23) {
|
|
||||||
_has_vcl = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_rtmp_packet) {
|
if (!_rtmp_packet) {
|
||||||
//I or P or B frame
|
//I or P or B frame
|
||||||
int8_t flags = FLV_CODEC_H265;
|
int8_t flags = FLV_CODEC_H265;
|
||||||
bool is_config = false;
|
bool is_config = false;
|
||||||
flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
|
flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
|
||||||
// todo 必须是IDR帧才能是关键帧,否则有可能开始帧会花屏 SPS PPS VPS 打头的是一般I帧,但不一定是IDR帧
|
|
||||||
// RtmpCodec::inputRtmp 时需要判断 是否是IDR帧,做出相应的修改
|
|
||||||
_rtmp_packet = RtmpPacket::create();
|
_rtmp_packet = RtmpPacket::create();
|
||||||
|
_rtmp_packet->buffer.clear();
|
||||||
_rtmp_packet->buffer.push_back(flags);
|
_rtmp_packet->buffer.push_back(flags);
|
||||||
_rtmp_packet->buffer.push_back(!is_config);
|
_rtmp_packet->buffer.push_back(!is_config);
|
||||||
int32_t cts = frame->pts() - frame->dts();
|
int32_t cts = frame->pts() - frame->dts();
|
||||||
@ -204,10 +182,13 @@ void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
|
|||||||
_rtmp_packet->time_stamp = frame->dts();
|
_rtmp_packet->time_stamp = frame->dts();
|
||||||
_rtmp_packet->type_id = MSG_VIDEO;
|
_rtmp_packet->type_id = MSG_VIDEO;
|
||||||
}
|
}
|
||||||
uint32_t size = htonl((uint32_t) len);
|
|
||||||
_rtmp_packet->buffer.append((char *) &size, 4);
|
_merger.inputFrame(frame, [&](uint32_t, uint32_t, const Buffer::Ptr &, bool) {
|
||||||
_rtmp_packet->buffer.append(data, len);
|
//输出rtmp packet
|
||||||
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
|
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
|
||||||
|
RtmpCodec::inputRtmp(_rtmp_packet);
|
||||||
|
_rtmp_packet = nullptr;
|
||||||
|
}, &_rtmp_packet->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void H265RtmpEncoder::makeVideoConfigPkt() {
|
void H265RtmpEncoder::makeVideoConfigPkt() {
|
||||||
|
@ -78,13 +78,13 @@ private:
|
|||||||
void makeVideoConfigPkt();
|
void makeVideoConfigPkt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _has_vcl = false;
|
|
||||||
bool _got_config_frame = false;
|
bool _got_config_frame = false;
|
||||||
string _vps;
|
string _vps;
|
||||||
string _sps;
|
string _sps;
|
||||||
string _pps;
|
string _pps;
|
||||||
H265Track::Ptr _track;
|
H265Track::Ptr _track;
|
||||||
RtmpPacket::Ptr _rtmp_packet;
|
RtmpPacket::Ptr _rtmp_packet;
|
||||||
|
FrameMerger _merger{FrameMerger::mp4_nal_size};
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
@ -269,19 +269,15 @@ void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) {
|
|||||||
//H265 数据
|
//H265 数据
|
||||||
memcpy(payload + 3, ptr + offset, max_size);
|
memcpy(payload + 3, ptr + offset, max_size);
|
||||||
//输入到rtp环形缓存
|
//输入到rtp环形缓存
|
||||||
RtpCodec::inputRtp(rtp, fu_start && H265Frame::isKeyFrame(nal_type, frame->data() + frame->prefixSize()));
|
RtpCodec::inputRtp(rtp, fu_start && frame->keyFrame());
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += max_size;
|
offset += max_size;
|
||||||
fu_start = false;
|
fu_start = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
makeH265Rtp(nal_type, ptr, len, false, true, pts);
|
RtpCodec::inputRtp(makeRtp(getTrackType(), ptr, len, false, pts), frame->keyFrame());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void H265RtpEncoder::makeH265Rtp(int nal_type,const void* data, size_t len, bool mark, bool first_packet, uint32_t uiStamp) {
|
|
||||||
RtpCodec::inputRtp(makeRtp(getTrackType(),data,len,mark,uiStamp),first_packet && H265Frame::isKeyFrame(nal_type, (const char*)data + prefixSize((const char*)data, len)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
@ -84,8 +84,6 @@ public:
|
|||||||
* @param frame 帧数据,必须
|
* @param frame 帧数据,必须
|
||||||
*/
|
*/
|
||||||
void inputFrame(const Frame::Ptr &frame) override;
|
void inputFrame(const Frame::Ptr &frame) override;
|
||||||
private:
|
|
||||||
void makeH265Rtp(int nal_type,const void *pData, size_t uiLen, bool bMark, bool first_packet,uint32_t uiStamp);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace mediakit{
|
}//namespace mediakit{
|
||||||
|
Loading…
Reference in New Issue
Block a user