mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
优化frame性能及整理代码
This commit is contained in:
parent
c5cfbce241
commit
62ba87dd0f
@ -87,7 +87,7 @@ void DevChannel::inputH264(const char *data, int len, uint32_t dts, uint32_t pts
|
||||
//由于rtmp/hls/mp4需要缓存时间戳相同的帧,
|
||||
//所以使用FrameNoCacheAble类型的帧反而会在转换成FrameCacheAble时多次内存拷贝
|
||||
//在此处只拷贝一次,性能开销更低
|
||||
H264Frame::Ptr frame = std::make_shared<H264Frame>();
|
||||
auto frame = FrameImp::create<H264Frame>();
|
||||
frame->_dts = dts;
|
||||
frame->_pts = pts;
|
||||
frame->_buffer.assign(data, len);
|
||||
@ -106,7 +106,7 @@ void DevChannel::inputH265(const char *data, int len, uint32_t dts, uint32_t pts
|
||||
//由于rtmp/hls/mp4需要缓存时间戳相同的帧,
|
||||
//所以使用FrameNoCacheAble类型的帧反而会在转换成FrameCacheAble时多次内存拷贝
|
||||
//在此处只拷贝一次,性能开销更低
|
||||
H265Frame::Ptr frame = std::make_shared<H265Frame>();
|
||||
auto frame = FrameImp::create<H265Frame>();
|
||||
frame->_dts = dts;
|
||||
frame->_pts = pts;
|
||||
frame->_buffer.assign(data, len);
|
||||
|
@ -182,6 +182,136 @@ bool parseAacConfig(const string &config, int &samplerate, int &channels){
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* aac类型SDP
|
||||
*/
|
||||
class AACSdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* 构造函数
|
||||
* @param aac_cfg aac两个字节的配置描述
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload type 默认98
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
AACSdp(const string &aac_cfg,
|
||||
int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " MPEG4-GENERIC/" << sample_rate << "/" << channels << "\r\n";
|
||||
|
||||
string configStr;
|
||||
char buf[4] = {0};
|
||||
for(auto &ch : aac_cfg){
|
||||
snprintf(buf, sizeof(buf), "%02X", (uint8_t)ch);
|
||||
configStr.append(buf);
|
||||
}
|
||||
_printer << "a=fmtp:" << payload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;"
|
||||
<< "sizelength=13;indexlength=3;indexdeltalength=3;config=" << configStr << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecAAC;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AACTrack::AACTrack(const string &aac_cfg) {
|
||||
if (aac_cfg.size() < 2) {
|
||||
throw std::invalid_argument("adts配置必须最少2个字节");
|
||||
}
|
||||
_cfg = aac_cfg;
|
||||
onReady();
|
||||
}
|
||||
|
||||
const string &AACTrack::getAacCfg() const {
|
||||
return _cfg;
|
||||
}
|
||||
|
||||
CodecId AACTrack::getCodecId() const {
|
||||
return CodecAAC;
|
||||
}
|
||||
|
||||
bool AACTrack::ready() {
|
||||
return !_cfg.empty();
|
||||
}
|
||||
|
||||
int AACTrack::getAudioSampleRate() const {
|
||||
return _sampleRate;
|
||||
}
|
||||
|
||||
int AACTrack::getAudioSampleBit() const {
|
||||
return _sampleBit;
|
||||
}
|
||||
|
||||
int AACTrack::getAudioChannel() const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
void AACTrack::inputFrame(const Frame::Ptr &frame) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void AACTrack::inputFrame_l(const Frame::Ptr &frame) {
|
||||
if (_cfg.empty()) {
|
||||
//未获取到aac_cfg信息
|
||||
if (frame->prefixSize()) {
|
||||
//根据7个字节的adts头生成aac config
|
||||
_cfg = makeAacConfig((uint8_t *) (frame->data()), frame->prefixSize());
|
||||
onReady();
|
||||
} else {
|
||||
WarnL << "无法获取adts头!";
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->size() > frame->prefixSize()) {
|
||||
//除adts头外,有实际负载
|
||||
AudioTrack::inputFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void AACTrack::onReady() {
|
||||
if (_cfg.size() < 2) {
|
||||
return;
|
||||
}
|
||||
parseAacConfig(_cfg, _sampleRate, _channel);
|
||||
}
|
||||
|
||||
Track::Ptr AACTrack::clone() {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type>(*this);
|
||||
}
|
||||
|
||||
Sdp::Ptr AACTrack::getSdp() {
|
||||
if(!ready()){
|
||||
WarnL << getCodecName() << " Track未准备好";
|
||||
|
@ -27,184 +27,43 @@ bool parseAacConfig(const string &config, int &samplerate, int &channels);
|
||||
*/
|
||||
class AACTrack : public AudioTrack{
|
||||
public:
|
||||
typedef std::shared_ptr<AACTrack> Ptr;
|
||||
using Ptr = std::shared_ptr<AACTrack>;
|
||||
|
||||
/**
|
||||
* 延后获取adts头信息
|
||||
* 在随后的inputFrame中获取adts头信息
|
||||
*/
|
||||
AACTrack(){}
|
||||
AACTrack() = default;
|
||||
|
||||
/**
|
||||
* 构造aac类型的媒体
|
||||
* @param aac_cfg aac配置信息
|
||||
*/
|
||||
AACTrack(const string &aac_cfg){
|
||||
setAacCfg(aac_cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置aac 配置信息
|
||||
*/
|
||||
void setAacCfg(const string &aac_cfg){
|
||||
if (aac_cfg.size() < 2) {
|
||||
throw std::invalid_argument("adts配置必须最少2个字节");
|
||||
}
|
||||
_cfg = aac_cfg;
|
||||
onReady();
|
||||
}
|
||||
AACTrack(const string &aac_cfg);
|
||||
|
||||
/**
|
||||
* 获取aac 配置信息
|
||||
*/
|
||||
const string &getAacCfg() const{
|
||||
return _cfg;
|
||||
}
|
||||
const string &getAacCfg() const;
|
||||
|
||||
/**
|
||||
* 返回编码类型
|
||||
*/
|
||||
CodecId getCodecId() const override{
|
||||
return CodecAAC;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在获取aac_cfg前是无效的Track
|
||||
*/
|
||||
bool ready() override {
|
||||
return !_cfg.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回音频采样率
|
||||
*/
|
||||
int getAudioSampleRate() const override{
|
||||
return _sampleRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回音频采样位数,一般为16或8
|
||||
*/
|
||||
int getAudioSampleBit() const override{
|
||||
return _sampleBit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回音频通道数
|
||||
*/
|
||||
int getAudioChannel() const override{
|
||||
return _channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入数据帧,并获取aac_cfg
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
bool ready() override;
|
||||
CodecId getCodecId() const override;
|
||||
int getAudioChannel() const override;
|
||||
int getAudioSampleRate() const override;
|
||||
int getAudioSampleBit() const override;
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
|
||||
private:
|
||||
void inputFrame_l(const Frame::Ptr &frame) {
|
||||
if (_cfg.empty()) {
|
||||
//未获取到aac_cfg信息
|
||||
if (frame->prefixSize()) {
|
||||
//根据7个字节的adts头生成aac config
|
||||
_cfg = makeAacConfig((uint8_t *) (frame->data()), frame->prefixSize());
|
||||
onReady();
|
||||
} else {
|
||||
WarnL << "无法获取adts头!";
|
||||
}
|
||||
}
|
||||
|
||||
if (frame->size() > frame->prefixSize()) {
|
||||
//除adts头外,有实际负载
|
||||
AudioTrack::inputFrame(frame);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 解析2个字节的aac配置
|
||||
*/
|
||||
void onReady(){
|
||||
if (_cfg.size() < 2) {
|
||||
return;
|
||||
}
|
||||
parseAacConfig(_cfg, _sampleRate, _channel);
|
||||
}
|
||||
|
||||
Track::Ptr clone() override {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
|
||||
}
|
||||
|
||||
//生成sdp
|
||||
void onReady();
|
||||
Sdp::Ptr getSdp() override;
|
||||
Track::Ptr clone() override;
|
||||
void inputFrame_l(const Frame::Ptr &frame);
|
||||
|
||||
private:
|
||||
string _cfg;
|
||||
int _channel = 0;
|
||||
int _sampleRate = 0;
|
||||
int _sampleBit = 16;
|
||||
int _channel = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* aac类型SDP
|
||||
*/
|
||||
class AACSdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* 构造函数
|
||||
* @param aac_cfg aac两个字节的配置描述
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload type 默认98
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
AACSdp(const string &aac_cfg,
|
||||
int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " MPEG4-GENERIC/" << sample_rate << "/" << channels << "\r\n";
|
||||
|
||||
string configStr;
|
||||
char buf[4] = {0};
|
||||
for(auto &ch : aac_cfg){
|
||||
snprintf(buf, sizeof(buf), "%02X", (uint8_t)ch);
|
||||
configStr.append(buf);
|
||||
}
|
||||
_printer << "a=fmtp:" << payload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;"
|
||||
<< "sizelength=13;indexlength=3;indexdeltalength=3;config=" << configStr << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecAAC;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
@ -42,7 +42,7 @@ void AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
|
||||
}
|
||||
|
||||
void AACRtmpDecoder::onGetAAC(const char* data, size_t len, uint32_t stamp) {
|
||||
auto frame = ResourcePoolHelper<FrameImp>::obtainObj();
|
||||
auto frame = FrameImp::create();
|
||||
frame->_codec_id = CodecAAC;
|
||||
|
||||
//生成adts头
|
||||
|
@ -19,7 +19,7 @@ namespace mediakit{
|
||||
/**
|
||||
* aac Rtmp转adts类
|
||||
*/
|
||||
class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper<FrameImp> {
|
||||
class AACRtmpDecoder : public RtmpCodec{
|
||||
public:
|
||||
typedef std::shared_ptr<AACRtmpDecoder> Ptr;
|
||||
|
||||
|
@ -74,9 +74,7 @@ AACRtpDecoder::AACRtpDecoder() {
|
||||
|
||||
void AACRtpDecoder::obtainFrame() {
|
||||
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
|
||||
_frame = ResourcePoolHelper<FrameImp>::obtainObj();
|
||||
_frame->_prefix_size = 0;
|
||||
_frame->_buffer.clear();
|
||||
_frame = FrameImp::create();
|
||||
_frame->_codec_id = CodecAAC;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ namespace mediakit{
|
||||
/**
|
||||
* aac rtp转adts类
|
||||
*/
|
||||
class AACRtpDecoder : public RtpCodec , public ResourcePoolHelper<FrameImp> {
|
||||
class AACRtpDecoder : public RtpCodec {
|
||||
public:
|
||||
typedef std::shared_ptr<AACRtpDecoder> Ptr;
|
||||
|
||||
|
@ -22,11 +22,8 @@ CodecId CommonRtmpDecoder::getCodecId() const {
|
||||
}
|
||||
|
||||
void CommonRtmpDecoder::obtainFrame() {
|
||||
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
|
||||
_frame = ResourcePoolHelper<FrameImp>::obtainObj();
|
||||
_frame->_buffer.clear();
|
||||
_frame = FrameImp::create();
|
||||
_frame->_codec_id = _codec;
|
||||
_frame->_prefix_size = 0;
|
||||
}
|
||||
|
||||
void CommonRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp) {
|
||||
|
@ -19,7 +19,7 @@ namespace mediakit{
|
||||
/**
|
||||
* 通用 rtmp解码类
|
||||
*/
|
||||
class CommonRtmpDecoder : public RtmpCodec , public ResourcePoolHelper<FrameImp> {
|
||||
class CommonRtmpDecoder : public RtmpCodec {
|
||||
public:
|
||||
typedef std::shared_ptr<CommonRtmpDecoder> Ptr;
|
||||
|
||||
|
@ -21,10 +21,7 @@ CodecId CommonRtpDecoder::getCodecId() const {
|
||||
}
|
||||
|
||||
void CommonRtpDecoder::obtainFrame() {
|
||||
_frame = ResourcePoolHelper<FrameImp>::obtainObj();
|
||||
_frame->_buffer.clear();
|
||||
_frame->_prefix_size = 0;
|
||||
_frame->_dts = 0;
|
||||
_frame = FrameImp::create();
|
||||
_frame->_codec_id = _codec;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace mediakit{
|
||||
/**
|
||||
* 通用 rtp解码类
|
||||
*/
|
||||
class CommonRtpDecoder : public RtpCodec, public ResourcePoolHelper<FrameImp> {
|
||||
class CommonRtpDecoder : public RtpCodec {
|
||||
public:
|
||||
typedef std::shared_ptr <CommonRtpDecoder> Ptr;
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
*/
|
||||
|
||||
#include "Frame.h"
|
||||
#include "H264.h"
|
||||
#include "H265.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
@ -20,6 +22,34 @@ namespace toolkit {
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
template<typename C>
|
||||
std::shared_ptr<C> FrameImp::create_l() {
|
||||
#if 0
|
||||
static ResourcePool<C> packet_pool;
|
||||
static onceToken token([]() {
|
||||
packet_pool.setSize(1024);
|
||||
});
|
||||
auto ret = packet_pool.obtain();
|
||||
ret->_buffer.clear();
|
||||
ret->_prefix_size = 0;
|
||||
ret->_dts = 0;
|
||||
ret->_pts = 0;
|
||||
return ret;
|
||||
#else
|
||||
return std::shared_ptr<C>(new C());
|
||||
#endif
|
||||
}
|
||||
|
||||
#define CREATE_FRAME_IMP(C) \
|
||||
template<> \
|
||||
std::shared_ptr<C> FrameImp::create<C>() { \
|
||||
return create_l<C>(); \
|
||||
}
|
||||
|
||||
CREATE_FRAME_IMP(FrameImp);
|
||||
CREATE_FRAME_IMP(H264Frame);
|
||||
CREATE_FRAME_IMP(H265Frame);
|
||||
|
||||
/**
|
||||
* 该对象的功能是把一个不可缓存的帧转换成可缓存的帧
|
||||
*/
|
||||
@ -32,8 +62,8 @@ public:
|
||||
_frame = frame;
|
||||
_ptr = frame->data();
|
||||
}else{
|
||||
_buffer = std::make_shared<BufferRaw>();
|
||||
_buffer->assign(frame->data(),frame->size());
|
||||
_buffer = FrameImp::create();
|
||||
_buffer->_buffer.assign(frame->data(),frame->size());
|
||||
_ptr = _buffer->data();
|
||||
}
|
||||
_size = frame->size();
|
||||
@ -66,7 +96,7 @@ private:
|
||||
bool _key;
|
||||
bool _config;
|
||||
Frame::Ptr _frame;
|
||||
BufferRaw::Ptr _buffer;
|
||||
FrameImp::Ptr _buffer;
|
||||
};
|
||||
|
||||
Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){
|
||||
|
@ -130,7 +130,10 @@ private:
|
||||
|
||||
class FrameImp : public Frame {
|
||||
public:
|
||||
typedef std::shared_ptr<FrameImp> Ptr;
|
||||
using Ptr = std::shared_ptr<FrameImp>;
|
||||
|
||||
template<typename C=FrameImp>
|
||||
static std::shared_ptr<C> create();
|
||||
|
||||
char *data() const override{
|
||||
return (char *)_buffer.data();
|
||||
@ -172,6 +175,13 @@ public:
|
||||
BufferLikeString _buffer;
|
||||
//对象个数统计
|
||||
ObjectStatistic<FrameImp> _statistic;
|
||||
|
||||
protected:
|
||||
friend class ResourcePool_l<FrameImp>;
|
||||
FrameImp() = default;
|
||||
|
||||
template<typename C>
|
||||
static std::shared_ptr<C> create_l();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -195,26 +205,6 @@ private:
|
||||
Frame::Ptr _parent_frame;
|
||||
};
|
||||
|
||||
/**
|
||||
* 循环池辅助类
|
||||
*/
|
||||
template <typename T>
|
||||
class ResourcePoolHelper{
|
||||
public:
|
||||
ResourcePoolHelper(int size = 0){
|
||||
if (size > 0) {
|
||||
_pool.setSize(size);
|
||||
}
|
||||
}
|
||||
virtual ~ResourcePoolHelper(){}
|
||||
|
||||
std::shared_ptr<T> obtainObj(){
|
||||
return _pool.obtain();
|
||||
}
|
||||
private:
|
||||
ResourcePool<T> _pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* 写帧接口的抽象接口类
|
||||
*/
|
||||
|
@ -12,6 +12,47 @@
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
/**
|
||||
* G711类型SDP
|
||||
*/
|
||||
class G711Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* G711采样率固定为8000
|
||||
* @param codecId G711A G711U
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
G711Sdp(CodecId codecId,
|
||||
int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type), _codecId(codecId){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << (codecId == CodecG711A ? " PCMA/" : " PCMU/") << sample_rate << "/" << channels << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return _codecId;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
CodecId _codecId;
|
||||
};
|
||||
|
||||
Track::Ptr G711Track::clone() {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type>(*this);
|
||||
}
|
||||
|
||||
Sdp::Ptr G711Track::getSdp() {
|
||||
if(!ready()){
|
||||
WarnL << getCodecName() << " Track未准备好";
|
||||
|
@ -21,53 +21,12 @@ namespace mediakit{
|
||||
*/
|
||||
class G711Track : public AudioTrackImp{
|
||||
public:
|
||||
typedef std::shared_ptr<G711Track> Ptr;
|
||||
using Ptr = std::shared_ptr<G711Track>;
|
||||
G711Track(CodecId codecId,int sample_rate, int channels, int sample_bit) : AudioTrackImp(codecId,sample_rate,channels,sample_bit){}
|
||||
|
||||
private:
|
||||
//克隆该Track
|
||||
Track::Ptr clone() override {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
|
||||
}
|
||||
//生成sdp
|
||||
Sdp::Ptr getSdp() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* G711类型SDP
|
||||
*/
|
||||
class G711Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* G711采样率固定为8000
|
||||
* @param codecId G711A G711U
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
G711Sdp(CodecId codecId,
|
||||
int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type), _codecId(codecId){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << (codecId == CodecG711A ? " PCMA/" : " PCMU/") << sample_rate << "/" << channels << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return _codecId;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
CodecId _codecId;
|
||||
Track::Ptr clone() override;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
@ -96,6 +96,174 @@ size_t prefixSize(const char *ptr, size_t len){
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
H264Track::H264Track(const string &sps,const string &pps,int sps_prefix_len,int pps_prefix_len){
|
||||
_sps = sps.substr(sps_prefix_len);
|
||||
_pps = pps.substr(pps_prefix_len);
|
||||
onReady();
|
||||
}
|
||||
|
||||
H264Track::H264Track(const Frame::Ptr &sps,const Frame::Ptr &pps){
|
||||
if(sps->getCodecId() != CodecH264 || pps->getCodecId() != CodecH264 ){
|
||||
throw std::invalid_argument("必须输入H264类型的帧");
|
||||
}
|
||||
_sps = string(sps->data() + sps->prefixSize(),sps->size() - sps->prefixSize());
|
||||
_pps = string(pps->data() + pps->prefixSize(),pps->size() - pps->prefixSize());
|
||||
onReady();
|
||||
}
|
||||
|
||||
const string &H264Track::getSps() const{
|
||||
return _sps;
|
||||
}
|
||||
|
||||
const string &H264Track::getPps() const{
|
||||
return _pps;
|
||||
}
|
||||
|
||||
CodecId H264Track::getCodecId() const {
|
||||
return CodecH264;
|
||||
}
|
||||
|
||||
int H264Track::getVideoHeight() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
int H264Track::getVideoWidth() const {
|
||||
return _width;
|
||||
}
|
||||
|
||||
float H264Track::getVideoFps() const {
|
||||
return _fps;
|
||||
}
|
||||
|
||||
bool H264Track::ready() {
|
||||
return !_sps.empty() && !_pps.empty();
|
||||
}
|
||||
|
||||
void H264Track::inputFrame(const Frame::Ptr &frame) {
|
||||
using H264FrameInternal = FrameInternal<H264FrameNoCacheAble>;
|
||||
|
||||
int type = H264_TYPE(*((uint8_t *) frame->data() + frame->prefixSize()));
|
||||
if (type != H264Frame::NAL_B_P && type != H264Frame::NAL_IDR) {
|
||||
//非I/B/P帧情况下,split一下,防止多个帧粘合在一起
|
||||
splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, size_t len, size_t prefix) {
|
||||
H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame, (char *) ptr, len, prefix);
|
||||
inputFrame_l(sub_frame);
|
||||
});
|
||||
} else {
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void H264Track::onReady(){
|
||||
getAVCInfo(_sps,_width,_height,_fps);
|
||||
}
|
||||
|
||||
Track::Ptr H264Track::clone() {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
|
||||
}
|
||||
|
||||
void H264Track::inputFrame_l(const Frame::Ptr &frame){
|
||||
int type = H264_TYPE(*((uint8_t *) frame->data() + frame->prefixSize()));
|
||||
switch (type) {
|
||||
case H264Frame::NAL_SPS: {
|
||||
_sps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
||||
break;
|
||||
}
|
||||
case H264Frame::NAL_PPS: {
|
||||
_pps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
||||
break;
|
||||
}
|
||||
case H264Frame::NAL_IDR: {
|
||||
insertConfigFrame(frame);
|
||||
VideoTrack::inputFrame(frame);
|
||||
break;
|
||||
}
|
||||
case H264Frame::NAL_AUD: {
|
||||
//忽略AUD帧;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
VideoTrack::inputFrame(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
_is_idr = type == H264Frame::NAL_IDR;
|
||||
if (_width == 0 && ready()) {
|
||||
onReady();
|
||||
}
|
||||
}
|
||||
|
||||
void H264Track::insertConfigFrame(const Frame::Ptr &frame){
|
||||
if(_is_idr){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_sps.empty()){
|
||||
auto spsFrame = FrameImp::create<H264Frame>();
|
||||
spsFrame->_prefix_size = 4;
|
||||
spsFrame->_buffer.assign("\x00\x00\x00\x01",4);
|
||||
spsFrame->_buffer.append(_sps);
|
||||
spsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(spsFrame);
|
||||
}
|
||||
|
||||
if(!_pps.empty()){
|
||||
auto ppsFrame = FrameImp::create<H264Frame>();
|
||||
ppsFrame->_prefix_size = 4;
|
||||
ppsFrame->_buffer.assign("\x00\x00\x00\x01",4);
|
||||
ppsFrame->_buffer.append(_pps);
|
||||
ppsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(ppsFrame);
|
||||
}
|
||||
}
|
||||
|
||||
class H264Sdp : public Sdp {
|
||||
public:
|
||||
H264Sdp(const string &strSPS, const string &strPPS, int bitrate, int payload_type = 96)
|
||||
: Sdp(90000, payload_type) {
|
||||
//视频通道
|
||||
_printer << "m=video 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " H264/" << 90000 << "\r\n";
|
||||
_printer << "a=fmtp:" << payload_type << " packetization-mode=1; profile-level-id=";
|
||||
|
||||
char strTemp[100];
|
||||
uint32_t profile_level_id = 0;
|
||||
if (strSPS.length() >= 4) { // sanity check
|
||||
profile_level_id = (uint8_t(strSPS[1]) << 16) |
|
||||
(uint8_t(strSPS[2]) << 8) |
|
||||
(uint8_t(strSPS[3])); // profile_idc|constraint_setN_flag|level_idc
|
||||
}
|
||||
memset(strTemp, 0, 100);
|
||||
snprintf(strTemp, sizeof(strTemp), "%06X", profile_level_id);
|
||||
_printer << strTemp;
|
||||
_printer << "; sprop-parameter-sets=";
|
||||
memset(strTemp, 0, 100);
|
||||
av_base64_encode(strTemp, 100, (uint8_t *) strSPS.data(), (int) strSPS.size());
|
||||
_printer << strTemp << ",";
|
||||
memset(strTemp, 0, 100);
|
||||
av_base64_encode(strTemp, 100, (uint8_t *) strPPS.data(), (int) strPPS.size());
|
||||
_printer << strTemp << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int) TrackVideo << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const {
|
||||
return CodecH264;
|
||||
}
|
||||
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
Sdp::Ptr H264Track::getSdp() {
|
||||
if(!ready()){
|
||||
WarnL << getCodecName() << " Track未准备好";
|
||||
@ -103,6 +271,48 @@ Sdp::Ptr H264Track::getSdp() {
|
||||
}
|
||||
return std::make_shared<H264Sdp>(getSps(), getPps(), getBitRate() / 1024);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool H264Frame::keyFrame() const {
|
||||
return H264_TYPE(_buffer[_prefix_size]) == H264Frame::NAL_IDR;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -14,45 +14,38 @@
|
||||
#include "Frame.h"
|
||||
#include "Track.h"
|
||||
#include "Util/base64.h"
|
||||
using namespace toolkit;
|
||||
#define H264_TYPE(v) ((uint8_t)(v) & 0x1F)
|
||||
using namespace toolkit;
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
bool getAVCInfo(const string &strSps,int &iVideoWidth, int &iVideoHeight, float &iVideoFps);
|
||||
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);
|
||||
|
||||
/**
|
||||
* 264帧类
|
||||
*/
|
||||
class H264Frame : public FrameImp {
|
||||
public:
|
||||
typedef std::shared_ptr<H264Frame> Ptr;
|
||||
using Ptr = std::shared_ptr<H264Frame>;
|
||||
|
||||
typedef enum {
|
||||
enum {
|
||||
NAL_IDR = 5,
|
||||
NAL_SEI = 6,
|
||||
NAL_SPS = 7,
|
||||
NAL_PPS = 8,
|
||||
NAL_AUD = 9,
|
||||
NAL_B_P = 1,
|
||||
} NalType;
|
||||
};
|
||||
|
||||
H264Frame(){
|
||||
_codec_id = CodecH264;
|
||||
}
|
||||
bool keyFrame() const override;
|
||||
bool configFrame() const override;
|
||||
|
||||
bool keyFrame() const override {
|
||||
return H264_TYPE(_buffer[_prefix_size]) == H264Frame::NAL_IDR;
|
||||
}
|
||||
|
||||
bool configFrame() const override{
|
||||
switch(H264_TYPE(_buffer[_prefix_size]) ){
|
||||
case H264Frame::NAL_SPS:
|
||||
case H264Frame::NAL_PPS:return true;
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
protected:
|
||||
friend class FrameImp;
|
||||
friend class ResourcePool_l<H264Frame>;
|
||||
H264Frame();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -62,44 +55,26 @@ public:
|
||||
*/
|
||||
class H264FrameNoCacheAble : public FrameFromPtr {
|
||||
public:
|
||||
typedef std::shared_ptr<H264FrameNoCacheAble> Ptr;
|
||||
using Ptr = std::shared_ptr<H264FrameNoCacheAble>;
|
||||
|
||||
H264FrameNoCacheAble(char *ptr,size_t size,uint32_t dts , uint32_t pts ,size_t prefix_size = 4){
|
||||
_ptr = ptr;
|
||||
_size = size;
|
||||
_dts = dts;
|
||||
_pts = pts;
|
||||
_prefix_size = prefix_size;
|
||||
_codec_id = CodecH264;
|
||||
}
|
||||
|
||||
bool keyFrame() const override {
|
||||
return H264_TYPE(_ptr[_prefix_size]) == H264Frame::NAL_IDR;
|
||||
}
|
||||
|
||||
bool configFrame() const override{
|
||||
switch(H264_TYPE(_ptr[_prefix_size])){
|
||||
case H264Frame::NAL_SPS:
|
||||
case H264Frame::NAL_PPS:return true;
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
typedef FrameInternal<H264FrameNoCacheAble> H264FrameInternal;
|
||||
|
||||
/**
|
||||
* 264视频通道
|
||||
*/
|
||||
class H264Track : public VideoTrack{
|
||||
public:
|
||||
typedef std::shared_ptr<H264Track> Ptr;
|
||||
using Ptr = std::shared_ptr<H264Track>;
|
||||
|
||||
/**
|
||||
* 不指定sps pps构造h264类型的媒体
|
||||
* 在随后的inputFrame中获取sps pps
|
||||
*/
|
||||
H264Track(){}
|
||||
H264Track() = default;
|
||||
|
||||
/**
|
||||
* 构造h264类型的媒体
|
||||
* @param sps sps帧数据
|
||||
@ -107,230 +82,42 @@ public:
|
||||
* @param sps_prefix_len 264头长度,可以为3个或4个字节,一般为0x00 00 00 01
|
||||
* @param pps_prefix_len 264头长度,可以为3个或4个字节,一般为0x00 00 00 01
|
||||
*/
|
||||
H264Track(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);
|
||||
onReady();
|
||||
}
|
||||
H264Track(const string &sps,const string &pps,int sps_prefix_len = 4,int pps_prefix_len = 4);
|
||||
|
||||
/**
|
||||
* 构造h264类型的媒体
|
||||
* @param sps sps帧
|
||||
* @param pps pps帧
|
||||
*/
|
||||
H264Track(const Frame::Ptr &sps,const Frame::Ptr &pps){
|
||||
if(sps->getCodecId() != CodecH264 || pps->getCodecId() != CodecH264 ){
|
||||
throw std::invalid_argument("必须输入H264类型的帧");
|
||||
}
|
||||
_sps = string(sps->data() + sps->prefixSize(),sps->size() - sps->prefixSize());
|
||||
_pps = string(pps->data() + pps->prefixSize(),pps->size() - pps->prefixSize());
|
||||
onReady();
|
||||
}
|
||||
H264Track(const Frame::Ptr &sps,const Frame::Ptr &pps);
|
||||
|
||||
/**
|
||||
* 返回不带0x00 00 00 01头的sps
|
||||
* @return
|
||||
* 返回不带0x00 00 00 01头的sps/pps
|
||||
*/
|
||||
const string &getSps() const{
|
||||
return _sps;
|
||||
}
|
||||
const string &getSps() const;
|
||||
const string &getPps() const;
|
||||
|
||||
/**
|
||||
* 返回不带0x00 00 00 01头的pps
|
||||
* @return
|
||||
*/
|
||||
const string &getPps() const{
|
||||
return _pps;
|
||||
}
|
||||
bool ready() override;
|
||||
CodecId getCodecId() const override;
|
||||
int getVideoHeight() const override;
|
||||
int getVideoWidth() const override;
|
||||
float getVideoFps() const override;
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecH264;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视频高度
|
||||
* @return
|
||||
*/
|
||||
int getVideoHeight() const override{
|
||||
return _height ;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视频宽度
|
||||
* @return
|
||||
*/
|
||||
int getVideoWidth() const override{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视频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 = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
|
||||
if(type != H264Frame::NAL_B_P && type != H264Frame::NAL_IDR){
|
||||
//非I/B/P帧情况下,split一下,防止多个帧粘合在一起
|
||||
splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, size_t len, size_t prefix) {
|
||||
H264FrameInternal::Ptr sub_frame = std::make_shared<H264FrameInternal>(frame, (char *)ptr, len, prefix);
|
||||
inputFrame_l(sub_frame);
|
||||
});
|
||||
} else{
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
}
|
||||
private:
|
||||
/**
|
||||
* 解析sps获取宽高fps
|
||||
*/
|
||||
void onReady(){
|
||||
getAVCInfo(_sps,_width,_height,_fps);
|
||||
}
|
||||
Track::Ptr clone() override {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入数据帧,并获取sps pps
|
||||
* @param frame 数据帧
|
||||
*/
|
||||
void inputFrame_l(const Frame::Ptr &frame){
|
||||
int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
|
||||
switch (type){
|
||||
case H264Frame::NAL_SPS:{
|
||||
//sps
|
||||
_sps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
|
||||
}
|
||||
break;
|
||||
case H264Frame::NAL_PPS:{
|
||||
//pps
|
||||
_pps = string(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
|
||||
}
|
||||
break;
|
||||
|
||||
case H264Frame::NAL_IDR:{
|
||||
//I
|
||||
insertConfigFrame(frame);
|
||||
VideoTrack::inputFrame(frame);
|
||||
}
|
||||
break;
|
||||
case H264Frame::NAL_AUD:{
|
||||
//忽略AUD帧;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VideoTrack::inputFrame(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
_last_frame_is_idr = type == H264Frame::NAL_IDR;
|
||||
if(_width == 0 && ready()){
|
||||
onReady();
|
||||
}
|
||||
}
|
||||
|
||||
//生成sdp
|
||||
void onReady();
|
||||
Sdp::Ptr getSdp() override;
|
||||
private:
|
||||
//在idr帧前插入sps pps帧
|
||||
void insertConfigFrame(const Frame::Ptr &frame){
|
||||
if(_last_frame_is_idr){
|
||||
return;
|
||||
}
|
||||
Track::Ptr clone() override;
|
||||
void inputFrame_l(const Frame::Ptr &frame);
|
||||
void insertConfigFrame(const Frame::Ptr &frame);
|
||||
|
||||
if(!_sps.empty()){
|
||||
auto spsFrame = std::make_shared<H264Frame>();
|
||||
spsFrame->_prefix_size = 4;
|
||||
spsFrame->_buffer.assign("\x0\x0\x0\x1",4);
|
||||
spsFrame->_buffer.append(_sps);
|
||||
spsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(spsFrame);
|
||||
}
|
||||
|
||||
if(!_pps.empty()){
|
||||
auto ppsFrame = std::make_shared<H264Frame>();
|
||||
ppsFrame->_prefix_size = 4;
|
||||
ppsFrame->_buffer.assign("\x0\x0\x0\x1",4);
|
||||
ppsFrame->_buffer.append(_pps);
|
||||
ppsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(ppsFrame);
|
||||
}
|
||||
}
|
||||
private:
|
||||
string _sps;
|
||||
string _pps;
|
||||
bool _is_idr = false;
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
float _fps = 0;
|
||||
bool _last_frame_is_idr = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* h264类型sdp
|
||||
*/
|
||||
class H264Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
*
|
||||
* @param sps 264 sps,不带0x00000001头
|
||||
* @param pps 264 pps,不带0x00000001头
|
||||
* @param payload_type rtp payload type 默认96
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
H264Sdp(const string &strSPS,
|
||||
const string &strPPS,
|
||||
int bitrate = 4000,
|
||||
int payload_type = 96) : Sdp(90000,payload_type) {
|
||||
//视频通道
|
||||
_printer << "m=video 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " H264/" << 90000 << "\r\n";
|
||||
_printer << "a=fmtp:" << payload_type << " packetization-mode=1; profile-level-id=";
|
||||
|
||||
char strTemp[100];
|
||||
uint32_t profile_level_id = 0;
|
||||
if (strSPS.length() >= 4) { // sanity check
|
||||
profile_level_id = (uint8_t(strSPS[1]) << 16) |
|
||||
(uint8_t(strSPS[2]) << 8) |
|
||||
(uint8_t(strSPS[3])); // profile_idc|constraint_setN_flag|level_idc
|
||||
}
|
||||
memset(strTemp, 0, 100);
|
||||
snprintf(strTemp, sizeof(strTemp), "%06X", profile_level_id);
|
||||
_printer << strTemp;
|
||||
_printer << "; sprop-parameter-sets=";
|
||||
memset(strTemp, 0, 100);
|
||||
av_base64_encode(strTemp, 100, (uint8_t *) strSPS.data(), (int)strSPS.size());
|
||||
_printer << strTemp << ",";
|
||||
memset(strTemp, 0, 100);
|
||||
av_base64_encode(strTemp, 100, (uint8_t *) strPPS.data(), (int)strPPS.size());
|
||||
_printer << strTemp << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackVideo << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecH264;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
string _sps;
|
||||
string _pps;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
@ -16,9 +16,7 @@ H264RtmpDecoder::H264RtmpDecoder() {
|
||||
}
|
||||
|
||||
H264Frame::Ptr H264RtmpDecoder::obtainFrame() {
|
||||
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
|
||||
auto frame = obtainObj();
|
||||
frame->_buffer.clear();
|
||||
auto frame = FrameImp::create<H264Frame>();
|
||||
frame->_prefix_size = 4;
|
||||
return frame;
|
||||
}
|
||||
@ -124,7 +122,7 @@ inline void H264RtmpDecoder::onGetH264(const char* pcData, size_t iLen, uint32_t
|
||||
#if 1
|
||||
_h264frame->_dts = dts;
|
||||
_h264frame->_pts = pts;
|
||||
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4); //添加264头
|
||||
_h264frame->_buffer.assign("\x00\x00\x00\x01", 4); //添加264头
|
||||
_h264frame->_buffer.append(pcData, iLen);
|
||||
|
||||
//写入环形缓存
|
||||
|
@ -22,7 +22,7 @@ namespace mediakit{
|
||||
* h264 Rtmp解码类
|
||||
* 将 h264 over rtmp 解复用出 h264-Frame
|
||||
*/
|
||||
class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper<H264Frame> {
|
||||
class H264RtmpDecoder : public RtmpCodec {
|
||||
public:
|
||||
typedef std::shared_ptr<H264RtmpDecoder> Ptr;
|
||||
|
||||
|
@ -35,9 +35,7 @@ H264RtpDecoder::H264RtpDecoder() {
|
||||
}
|
||||
|
||||
H264Frame::Ptr H264RtpDecoder::obtainFrame() {
|
||||
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
|
||||
auto frame = ResourcePoolHelper<H264Frame>::obtainObj();
|
||||
frame->_buffer.clear();
|
||||
auto frame = FrameImp::create<H264Frame>();
|
||||
frame->_prefix_size = 4;
|
||||
return frame;
|
||||
}
|
||||
@ -81,7 +79,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
|
||||
|
||||
if (nal_type >= 0 && nal_type < 24) {
|
||||
//a full frame
|
||||
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
|
||||
_h264frame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
_h264frame->_buffer.append((char *) frame, length);
|
||||
_h264frame->_pts = stamp;
|
||||
auto key = _h264frame->keyFrame();
|
||||
@ -108,7 +106,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
|
||||
}
|
||||
if (len > 0) {
|
||||
//有有效数据
|
||||
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
|
||||
_h264frame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
_h264frame->_buffer.append((char *) ptr, len);
|
||||
_h264frame->_pts = stamp;
|
||||
if ((ptr[0] & 0x1F) == H264Frame::NAL_IDR) {
|
||||
@ -127,7 +125,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
|
||||
MakeFU(frame[1], fu);
|
||||
if (fu.S) {
|
||||
//该帧的第一个rtp包 FU-A start
|
||||
_h264frame->_buffer.assign("\x0\x0\x0\x1", 4);
|
||||
_h264frame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
_h264frame->_buffer.push_back(nal_suffix | fu.type);
|
||||
_h264frame->_buffer.append((char *) frame + 2, length - 2);
|
||||
_h264frame->_pts = stamp;
|
||||
|
@ -24,7 +24,7 @@ namespace mediakit{
|
||||
* 将 h264 over rtsp-rtp 解复用出 h264-Frame
|
||||
* rfc3984
|
||||
*/
|
||||
class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper<H264Frame> {
|
||||
class H264RtpDecoder : public RtpCodec{
|
||||
public:
|
||||
typedef std::shared_ptr<H264RtpDecoder> Ptr;
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "H265.h"
|
||||
#include "SPSParser.h"
|
||||
#include "Util/logger.h"
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
@ -42,7 +41,6 @@ bool getHEVCInfo(const char * vps, size_t vps_len,const char * sps,size_t sps_le
|
||||
h265GetWidthHeight(&tH265SpsInfo, &iVideoWidth, &iVideoHeight);
|
||||
iVideoFps = 0;
|
||||
h265GeFramerate(&tH265VpsInfo, &tH265SpsInfo, &iVideoFps);
|
||||
// ErrorL << iVideoWidth << " " << iVideoHeight << " " << iVideoFps;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -50,6 +48,228 @@ bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, i
|
||||
return getHEVCInfo(strVps.data(), strVps.size(), strSps.data(), strSps.size(), iVideoWidth, iVideoHeight,iVideoFps);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool H265Frame::keyFrame() const {
|
||||
return isKeyFrame(H265_TYPE(_buffer[_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) {
|
||||
return type >= NAL_BLA_W_LP && type <= NAL_RSV_IRAP_VCL23;
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
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) {
|
||||
_vps = vps.substr(vps_prefix_len);
|
||||
_sps = sps.substr(sps_prefix_len);
|
||||
_pps = pps.substr(pps_prefix_len);
|
||||
onReady();
|
||||
}
|
||||
|
||||
const string &H265Track::getVps() const {
|
||||
return _vps;
|
||||
}
|
||||
|
||||
const string &H265Track::getSps() const {
|
||||
return _sps;
|
||||
}
|
||||
|
||||
const string &H265Track::getPps() const {
|
||||
return _pps;
|
||||
}
|
||||
|
||||
CodecId H265Track::getCodecId() const {
|
||||
return CodecH265;
|
||||
}
|
||||
|
||||
int H265Track::getVideoHeight() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
int H265Track::getVideoWidth() const {
|
||||
return _width;
|
||||
}
|
||||
|
||||
float H265Track::getVideoFps() const {
|
||||
return _fps;
|
||||
}
|
||||
|
||||
bool H265Track::ready() {
|
||||
return !_vps.empty() && !_sps.empty() && !_pps.empty();
|
||||
}
|
||||
|
||||
void H265Track::inputFrame(const Frame::Ptr &frame) {
|
||||
using H265FrameInternal = FrameInternal<H265FrameNoCacheAble>;
|
||||
|
||||
int type = H265_TYPE(*((uint8_t *) frame->data() + frame->prefixSize()));
|
||||
if (frame->configFrame() || type == H265Frame::NAL_SEI_PREFIX) {
|
||||
splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, size_t len, size_t prefix) {
|
||||
H265FrameInternal::Ptr sub_frame = std::make_shared<H265FrameInternal>(frame, (char *) ptr, len, prefix);
|
||||
inputFrame_l(sub_frame);
|
||||
});
|
||||
} else {
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void H265Track::inputFrame_l(const Frame::Ptr &frame) {
|
||||
int type = H265_TYPE(((uint8_t *) frame->data() + frame->prefixSize())[0]);
|
||||
if (H265Frame::isKeyFrame(type)) {
|
||||
insertConfigFrame(frame);
|
||||
VideoTrack::inputFrame(frame);
|
||||
_is_idr = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_is_idr = false;
|
||||
|
||||
//非idr帧
|
||||
switch (type) {
|
||||
case H265Frame::NAL_VPS: {
|
||||
_vps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
||||
break;
|
||||
}
|
||||
case H265Frame::NAL_SPS: {
|
||||
_sps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
||||
break;
|
||||
}
|
||||
case H265Frame::NAL_PPS: {
|
||||
_pps = string(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
VideoTrack::inputFrame(frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_width == 0 && ready()) {
|
||||
onReady();
|
||||
}
|
||||
}
|
||||
|
||||
void H265Track::onReady() {
|
||||
getHEVCInfo(_vps, _sps, _width, _height, _fps);
|
||||
}
|
||||
|
||||
Track::Ptr H265Track::clone() {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type>(*this);
|
||||
}
|
||||
|
||||
void H265Track::insertConfigFrame(const Frame::Ptr &frame) {
|
||||
if (_is_idr) {
|
||||
return;
|
||||
}
|
||||
if (!_vps.empty()) {
|
||||
auto vpsFrame = FrameImp::create<H265Frame>();
|
||||
vpsFrame->_prefix_size = 4;
|
||||
vpsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
vpsFrame->_buffer.append(_vps);
|
||||
vpsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(vpsFrame);
|
||||
}
|
||||
if (!_sps.empty()) {
|
||||
auto spsFrame = FrameImp::create<H265Frame>();
|
||||
spsFrame->_prefix_size = 4;
|
||||
spsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
spsFrame->_buffer.append(_sps);
|
||||
spsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(spsFrame);
|
||||
}
|
||||
|
||||
if (!_pps.empty()) {
|
||||
auto ppsFrame = FrameImp::create<H265Frame>();
|
||||
ppsFrame->_prefix_size = 4;
|
||||
ppsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
ppsFrame->_buffer.append(_pps);
|
||||
ppsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(ppsFrame);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* h265类型sdp
|
||||
*/
|
||||
class H265Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* 构造函数
|
||||
* @param sps 265 sps,不带0x00000001头
|
||||
* @param pps 265 pps,不带0x00000001头
|
||||
* @param payload_type rtp payload type 默认96
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
H265Sdp(const string &strVPS,
|
||||
const string &strSPS,
|
||||
const string &strPPS,
|
||||
int bitrate = 4000,
|
||||
int payload_type = 96) : Sdp(90000,payload_type) {
|
||||
//视频通道
|
||||
_printer << "m=video 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " H265/" << 90000 << "\r\n";
|
||||
_printer << "a=fmtp:" << payload_type << " ";
|
||||
_printer << "sprop-vps=";
|
||||
_printer << encodeBase64(strVPS) << "; ";
|
||||
_printer << "sprop-sps=";
|
||||
_printer << encodeBase64(strSPS) << "; ";
|
||||
_printer << "sprop-pps=";
|
||||
_printer << encodeBase64(strPPS) << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackVideo << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecH265;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
Sdp::Ptr H265Track::getSdp() {
|
||||
if(!ready()){
|
||||
WarnL << getCodecName() << " Track未准备好";
|
||||
@ -57,5 +277,6 @@ Sdp::Ptr H265Track::getSdp() {
|
||||
}
|
||||
return std::make_shared<H265Sdp>(getVps(), getSps(), getPps(), getBitRate() / 1024);
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
#include "Track.h"
|
||||
#include "Util/base64.h"
|
||||
#include "H264.h"
|
||||
using namespace toolkit;
|
||||
#define H265_TYPE(v) (((uint8_t)(v) >> 1) & 0x3f)
|
||||
using namespace toolkit;
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
@ -27,9 +27,8 @@ bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, i
|
||||
*/
|
||||
class H265Frame : public FrameImp {
|
||||
public:
|
||||
typedef std::shared_ptr<H265Frame> Ptr;
|
||||
|
||||
typedef enum {
|
||||
using Ptr = std::shared_ptr<H265Frame>;
|
||||
enum {
|
||||
NAL_TRAIL_N = 0,
|
||||
NAL_TRAIL_R = 1,
|
||||
NAL_TSA_N = 2,
|
||||
@ -58,71 +57,39 @@ public:
|
||||
NAL_FD_NUT = 38,
|
||||
NAL_SEI_PREFIX = 39,
|
||||
NAL_SEI_SUFFIX = 40,
|
||||
} NaleType;
|
||||
};
|
||||
|
||||
H265Frame(){
|
||||
_codec_id = CodecH265;
|
||||
}
|
||||
bool keyFrame() const override;
|
||||
bool configFrame() const override;
|
||||
static bool isKeyFrame(int type);
|
||||
|
||||
bool keyFrame() const override {
|
||||
return isKeyFrame(H265_TYPE(_buffer[_prefix_size]));
|
||||
}
|
||||
|
||||
bool configFrame() const override{
|
||||
switch(H265_TYPE(_buffer[_prefix_size])){
|
||||
case H265Frame::NAL_VPS:
|
||||
case H265Frame::NAL_SPS:
|
||||
case H265Frame::NAL_PPS : return true;
|
||||
default : return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isKeyFrame(int type) {
|
||||
return type >= NAL_BLA_W_LP && type <= NAL_RSV_IRAP_VCL23;
|
||||
}
|
||||
protected:
|
||||
friend class FrameImp;
|
||||
friend class ResourcePool_l<H265Frame>;
|
||||
H265Frame();
|
||||
};
|
||||
|
||||
class H265FrameNoCacheAble : public FrameFromPtr {
|
||||
public:
|
||||
typedef std::shared_ptr<H265FrameNoCacheAble> Ptr;
|
||||
using Ptr = std::shared_ptr<H265FrameNoCacheAble>;
|
||||
|
||||
H265FrameNoCacheAble(char *ptr, size_t size, uint32_t dts,uint32_t pts, size_t prefix_size = 4) {
|
||||
_ptr = ptr;
|
||||
_size = size;
|
||||
_dts = dts;
|
||||
_pts = pts;
|
||||
_prefix_size = prefix_size;
|
||||
_codec_id = CodecH265;
|
||||
}
|
||||
|
||||
bool keyFrame() const override {
|
||||
return H265Frame::isKeyFrame(H265_TYPE(((uint8_t *) _ptr)[_prefix_size]));
|
||||
}
|
||||
|
||||
bool configFrame() const override{
|
||||
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;
|
||||
}
|
||||
}
|
||||
H265FrameNoCacheAble(char *ptr, size_t size, uint32_t dts,uint32_t pts, size_t prefix_size = 4);
|
||||
bool keyFrame() const override;
|
||||
bool configFrame() const override;
|
||||
};
|
||||
|
||||
typedef FrameInternal<H265FrameNoCacheAble> H265FrameInternal;
|
||||
|
||||
/**
|
||||
* 265视频通道
|
||||
*/
|
||||
class H265Track : public VideoTrack {
|
||||
public:
|
||||
typedef std::shared_ptr<H265Track> Ptr;
|
||||
using Ptr = std::shared_ptr<H265Track>;
|
||||
|
||||
/**
|
||||
* 不指定sps pps构造h265类型的媒体
|
||||
* 在随后的inputFrame中获取sps pps
|
||||
*/
|
||||
H265Track() {}
|
||||
H265Track() = default;
|
||||
|
||||
/**
|
||||
* 构造h265类型的媒体
|
||||
@ -133,221 +100,37 @@ public:
|
||||
* @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 &vps,const string &sps, const string &pps,int vps_prefix_len = 4, int sps_prefix_len = 4, int pps_prefix_len = 4) {
|
||||
_vps = vps.substr(vps_prefix_len);
|
||||
_sps = sps.substr(sps_prefix_len);
|
||||
_pps = pps.substr(pps_prefix_len);
|
||||
onReady();
|
||||
}
|
||||
H265Track(const string &vps,const string &sps, const string &pps,int vps_prefix_len = 4, int sps_prefix_len = 4, int pps_prefix_len = 4);
|
||||
|
||||
/**
|
||||
* 返回不带0x00 00 00 01头的vps
|
||||
* 返回不带0x00 00 00 01头的vps/sps/pps
|
||||
*/
|
||||
const string &getVps() const {
|
||||
return _vps;
|
||||
}
|
||||
const string &getVps() const;
|
||||
const string &getSps() const;
|
||||
const string &getPps() const;
|
||||
|
||||
/**
|
||||
* 返回不带0x00 00 00 01头的sps
|
||||
*/
|
||||
const string &getSps() const {
|
||||
return _sps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回不带0x00 00 00 01头的pps
|
||||
*/
|
||||
const string &getPps() const {
|
||||
return _pps;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecH265;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视频高度
|
||||
*/
|
||||
int getVideoHeight() const override{
|
||||
return _height ;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视频宽度
|
||||
*/
|
||||
int getVideoWidth() const override{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视频fps
|
||||
*/
|
||||
float getVideoFps() const override{
|
||||
return _fps;
|
||||
}
|
||||
|
||||
bool ready() override {
|
||||
return !_vps.empty() && !_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()));
|
||||
if(frame->configFrame() || type == H265Frame::NAL_SEI_PREFIX){
|
||||
splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, size_t len, size_t prefix){
|
||||
H265FrameInternal::Ptr sub_frame = std::make_shared<H265FrameInternal>(frame, (char*)ptr, len, prefix);
|
||||
inputFrame_l(sub_frame);
|
||||
});
|
||||
} else {
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
}
|
||||
bool ready() override;
|
||||
CodecId getCodecId() const override;
|
||||
int getVideoWidth() const override;
|
||||
int getVideoHeight() const override;
|
||||
float getVideoFps() const override;
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* 输入数据帧,并获取sps pps
|
||||
* @param frame 数据帧
|
||||
*/
|
||||
void inputFrame_l(const Frame::Ptr &frame) {
|
||||
int type = H265_TYPE(((uint8_t *) frame->data() + frame->prefixSize())[0]);
|
||||
if (H265Frame::isKeyFrame(type)) {
|
||||
insertConfigFrame(frame);
|
||||
VideoTrack::inputFrame(frame);
|
||||
_last_frame_is_idr = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_last_frame_is_idr = false;
|
||||
|
||||
//非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;
|
||||
}
|
||||
if (_width == 0 && ready()) {
|
||||
onReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析sps获取宽高fps
|
||||
*/
|
||||
void onReady(){
|
||||
getHEVCInfo(_vps, _sps, _width, _height, _fps);
|
||||
}
|
||||
Track::Ptr clone() override {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type>(*this);
|
||||
}
|
||||
|
||||
//生成sdp
|
||||
void onReady();
|
||||
Sdp::Ptr getSdp() override;
|
||||
Track::Ptr clone() override;
|
||||
void inputFrame_l(const Frame::Ptr &frame);
|
||||
void insertConfigFrame(const Frame::Ptr &frame);
|
||||
|
||||
//在idr帧前插入vps sps pps帧
|
||||
void insertConfigFrame(const Frame::Ptr &frame){
|
||||
if(_last_frame_is_idr){
|
||||
return;
|
||||
}
|
||||
if(!_vps.empty()){
|
||||
auto vpsFrame = std::make_shared<H265Frame>();
|
||||
vpsFrame->_prefix_size = 4;
|
||||
vpsFrame->_buffer.assign("\x0\x0\x0\x1", 4);
|
||||
vpsFrame->_buffer.append(_vps);
|
||||
vpsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(vpsFrame);
|
||||
}
|
||||
if (!_sps.empty()) {
|
||||
auto spsFrame = std::make_shared<H265Frame>();
|
||||
spsFrame->_prefix_size = 4;
|
||||
spsFrame->_buffer.assign("\x0\x0\x0\x1", 4);
|
||||
spsFrame->_buffer.append(_sps);
|
||||
spsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(spsFrame);
|
||||
}
|
||||
|
||||
if (!_pps.empty()) {
|
||||
auto ppsFrame = std::make_shared<H265Frame>();
|
||||
ppsFrame->_prefix_size = 4;
|
||||
ppsFrame->_buffer.assign("\x0\x0\x0\x1", 4);
|
||||
ppsFrame->_buffer.append(_pps);
|
||||
ppsFrame->_dts = frame->dts();
|
||||
VideoTrack::inputFrame(ppsFrame);
|
||||
}
|
||||
}
|
||||
private:
|
||||
string _vps;
|
||||
string _sps;
|
||||
string _pps;
|
||||
bool _is_idr = false;
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
float _fps = 0;
|
||||
bool _last_frame_is_idr = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* h265类型sdp
|
||||
*/
|
||||
class H265Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* 构造函数
|
||||
* @param sps 265 sps,不带0x00000001头
|
||||
* @param pps 265 pps,不带0x00000001头
|
||||
* @param payload_type rtp payload type 默认96
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
H265Sdp(const string &strVPS,
|
||||
const string &strSPS,
|
||||
const string &strPPS,
|
||||
int bitrate = 4000,
|
||||
int payload_type = 96) : Sdp(90000,payload_type) {
|
||||
//视频通道
|
||||
_printer << "m=video 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " H265/" << 90000 << "\r\n";
|
||||
_printer << "a=fmtp:" << payload_type << " ";
|
||||
_printer << "sprop-vps=";
|
||||
_printer << encodeBase64(strVPS) << "; ";
|
||||
_printer << "sprop-sps=";
|
||||
_printer << encodeBase64(strSPS) << "; ";
|
||||
_printer << "sprop-pps=";
|
||||
_printer << encodeBase64(strPPS) << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackVideo << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecH265;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
string _vps;
|
||||
string _sps;
|
||||
string _pps;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
@ -20,9 +20,7 @@ H265RtmpDecoder::H265RtmpDecoder() {
|
||||
}
|
||||
|
||||
H265Frame::Ptr H265RtmpDecoder::obtainFrame() {
|
||||
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
|
||||
auto frame = obtainObj();
|
||||
frame->_buffer.clear();
|
||||
auto frame = FrameImp::create<H265Frame>();
|
||||
frame->_prefix_size = 4;
|
||||
return frame;
|
||||
}
|
||||
@ -102,7 +100,7 @@ inline void H265RtmpDecoder::onGetH265(const char* pcData, size_t iLen, uint32_t
|
||||
#if 1
|
||||
_h265frame->_dts = dts;
|
||||
_h265frame->_pts = pts;
|
||||
_h265frame->_buffer.assign("\x0\x0\x0\x1", 4); //添加265头
|
||||
_h265frame->_buffer.assign("\x00\x00\x00\x01", 4); //添加265头
|
||||
_h265frame->_buffer.append(pcData, iLen);
|
||||
|
||||
//写入环形缓存
|
||||
|
@ -22,7 +22,7 @@ namespace mediakit{
|
||||
* h265 Rtmp解码类
|
||||
* 将 h265 over rtmp 解复用出 h265-Frame
|
||||
*/
|
||||
class H265RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper<H265Frame> {
|
||||
class H265RtmpDecoder : public RtmpCodec {
|
||||
public:
|
||||
typedef std::shared_ptr<H265RtmpDecoder> Ptr;
|
||||
|
||||
|
@ -30,9 +30,7 @@ H265RtpDecoder::H265RtpDecoder() {
|
||||
}
|
||||
|
||||
H265Frame::Ptr H265RtpDecoder::obtainFrame() {
|
||||
//从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象
|
||||
auto frame = ResourcePoolHelper<H265Frame>::obtainObj();
|
||||
frame->_buffer.clear();
|
||||
auto frame = FrameImp::create<H265Frame>();
|
||||
frame->_prefix_size = 4;
|
||||
return frame;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace mediakit{
|
||||
* 将 h265 over rtsp-rtp 解复用出 h265-Frame
|
||||
* 《草案(H265-over-RTP)draft-ietf-payload-rtp-h265-07.pdf》
|
||||
*/
|
||||
class H265RtpDecoder : public RtpCodec , public ResourcePoolHelper<H265Frame> {
|
||||
class H265RtpDecoder : public RtpCodec {
|
||||
public:
|
||||
typedef std::shared_ptr<H265RtpDecoder> Ptr;
|
||||
|
||||
|
@ -12,6 +12,43 @@
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
/**
|
||||
* L16类型SDP
|
||||
*/
|
||||
class L16Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* L16采样位数固定为16位
|
||||
* @param codecId CodecL16
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
L16Sdp(CodecId codecId,
|
||||
int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type), _codecId(codecId){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " L16/" << sample_rate << "/" << channels << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return _codecId;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
CodecId _codecId;
|
||||
};
|
||||
|
||||
Sdp::Ptr L16Track::getSdp() {
|
||||
WarnL << "Enter L16Track::getSdp function";
|
||||
if(!ready()){
|
||||
@ -21,6 +58,10 @@ Sdp::Ptr L16Track::getSdp() {
|
||||
return std::make_shared<L16Sdp>(getCodecId(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024);
|
||||
}
|
||||
|
||||
Track::Ptr L16Track::clone() {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
|
||||
|
||||
|
@ -21,53 +21,12 @@ namespace mediakit{
|
||||
*/
|
||||
class L16Track : public AudioTrackImp{
|
||||
public:
|
||||
typedef std::shared_ptr<L16Track> Ptr;
|
||||
using Ptr = std::shared_ptr<L16Track>;
|
||||
L16Track(int sample_rate, int channels) : AudioTrackImp(CodecL16,sample_rate,channels,16){}
|
||||
|
||||
private:
|
||||
//克隆该Track
|
||||
Track::Ptr clone() override {
|
||||
return std::make_shared<std::remove_reference<decltype(*this)>::type >(*this);
|
||||
}
|
||||
//生成sdp
|
||||
Sdp::Ptr getSdp() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* L16类型SDP
|
||||
*/
|
||||
class L16Sdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* L16采样位数固定为16位
|
||||
* @param codecId CodecL16
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
L16Sdp(CodecId codecId,
|
||||
int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type), _codecId(codecId){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " L16/" << sample_rate << "/" << channels << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return _codecId;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
CodecId _codecId;
|
||||
Track::Ptr clone() override;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
@ -12,6 +12,40 @@
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
/**
|
||||
* Opus类型SDP
|
||||
*/
|
||||
class OpusSdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* 构造opus sdp
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
OpusSdp(int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " opus/" << sample_rate << "/" << channels << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecOpus;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
Sdp::Ptr OpusTrack::getSdp() {
|
||||
if(!ready()){
|
||||
WarnL << getCodecName() << " Track未准备好";
|
||||
|
@ -33,39 +33,5 @@ private:
|
||||
Sdp::Ptr getSdp() override ;
|
||||
};
|
||||
|
||||
/**
|
||||
* Opus类型SDP
|
||||
*/
|
||||
class OpusSdp : public Sdp {
|
||||
public:
|
||||
/**
|
||||
* 构造opus sdp
|
||||
* @param sample_rate 音频采样率
|
||||
* @param payload_type rtp payload
|
||||
* @param bitrate 比特率
|
||||
*/
|
||||
OpusSdp(int sample_rate,
|
||||
int channels,
|
||||
int bitrate = 128,
|
||||
int payload_type = 98) : Sdp(sample_rate,payload_type){
|
||||
_printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n";
|
||||
if (bitrate) {
|
||||
_printer << "b=AS:" << bitrate << "\r\n";
|
||||
}
|
||||
_printer << "a=rtpmap:" << payload_type << " opus/" << sample_rate << "/" << channels << "\r\n";
|
||||
_printer << "a=control:trackID=" << (int)TrackAudio << "\r\n";
|
||||
}
|
||||
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
|
||||
CodecId getCodecId() const override {
|
||||
return CodecOpus;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
#endif //ZLMEDIAKIT_OPUS_H
|
||||
|
@ -221,7 +221,7 @@ Frame::Ptr MP4Demuxer::makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int6
|
||||
if (frame_len + offset + 4 > bytes) {
|
||||
return nullptr;
|
||||
}
|
||||
memcpy(data + offset, "\x0\x0\x0\x1", 4);
|
||||
memcpy(data + offset, "\x00\x00\x00\x01", 4);
|
||||
offset += (frame_len + 4);
|
||||
}
|
||||
if (codec == CodecH264) {
|
||||
|
@ -142,9 +142,7 @@ RtmpPacket::Ptr RtmpPacket::create(){
|
||||
ret->clear();
|
||||
return ret;
|
||||
#else
|
||||
auto ret = Ptr(new RtmpPacket);
|
||||
ret->clear();
|
||||
return ret;
|
||||
return Ptr(new RtmpPacket);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user