mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-10-31 00:37:39 +08:00
完善对无metadata的rtmp流的兼容性
This commit is contained in:
parent
e7f263b046
commit
41509800bc
@ -221,13 +221,27 @@ Track::Ptr Factory::getAudioTrackByAmf(const AMFValue& amf, int sample_rate, int
|
|||||||
return getTrackByCodecId(codecId, sample_rate, channels, sample_bit);
|
return getTrackByCodecId(codecId, sample_rate, channels, sample_bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) {
|
RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_encode) {
|
||||||
switch (track->getCodecId()){
|
switch (track->getCodecId()){
|
||||||
case CodecH264 : return std::make_shared<H264RtmpEncoder>(track);
|
case CodecH264 : return std::make_shared<H264RtmpEncoder>(track);
|
||||||
case CodecAAC : return std::make_shared<AACRtmpEncoder>(track);
|
case CodecAAC : return std::make_shared<AACRtmpEncoder>(track);
|
||||||
case CodecH265 : return std::make_shared<H265RtmpEncoder>(track);
|
case CodecH265 : return std::make_shared<H265RtmpEncoder>(track);
|
||||||
case CodecG711A :
|
case CodecG711A :
|
||||||
case CodecG711U : return std::make_shared<G711RtmpEncoder>(track);
|
case CodecG711U : {
|
||||||
|
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
|
||||||
|
if (is_encode && (audio_track->getAudioSampleRate() != 8000 ||
|
||||||
|
audio_track->getAudioChannel() != 1 ||
|
||||||
|
audio_track->getAudioSampleBit() != 16)) {
|
||||||
|
//rtmp对g711只支持8000/1/16规格,但是ZLMediaKit可以解析其他规格的G711
|
||||||
|
WarnL << "RTMP只支持8000/1/16规格的G711,目前规格是:"
|
||||||
|
<< audio_track->getAudioSampleRate() << "/"
|
||||||
|
<< audio_track->getAudioChannel() << "/"
|
||||||
|
<< audio_track->getAudioSampleBit()
|
||||||
|
<< ",该音频已被忽略";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_shared<G711RtmpEncoder>(track);
|
||||||
|
}
|
||||||
default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr;
|
default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,9 @@ public:
|
|||||||
/**
|
/**
|
||||||
* 根据Track获取Rtmp的编解码器
|
* 根据Track获取Rtmp的编解码器
|
||||||
* @param track 媒体描述对象
|
* @param track 媒体描述对象
|
||||||
|
* @param is_encode 是否为编码器还是解码器
|
||||||
*/
|
*/
|
||||||
static RtmpCodec::Ptr getRtmpCodecByTrack(const Track::Ptr &track);
|
static RtmpCodec::Ptr getRtmpCodecByTrack(const Track::Ptr &track, bool is_encode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据codecId获取rtmp的codec描述
|
* 根据codecId获取rtmp的codec描述
|
||||||
|
@ -100,4 +100,23 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Metadata::addTrack(AMFValue &metadata, const Track::Ptr &track) {
|
||||||
|
Metadata::Ptr new_metadata;
|
||||||
|
switch (track->getTrackType()) {
|
||||||
|
case TrackVideo: {
|
||||||
|
new_metadata = std::make_shared<VideoMeta>(dynamic_pointer_cast<VideoTrack>(track));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackAudio: {
|
||||||
|
new_metadata = std::make_shared<AudioMeta>(dynamic_pointer_cast<AudioTrack>(track));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_metadata->getMetadata().object_for_each([&](const std::string &key, const AMFValue &value) {
|
||||||
|
metadata.set(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
@ -220,6 +220,8 @@ public:
|
|||||||
const AMFValue &getMetadata() const{
|
const AMFValue &getMetadata() const{
|
||||||
return _metadata;
|
return _metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addTrack(AMFValue &metadata, const Track::Ptr &track);
|
||||||
protected:
|
protected:
|
||||||
AMFValue _metadata;
|
AMFValue _metadata;
|
||||||
};
|
};
|
||||||
@ -261,7 +263,6 @@ private:
|
|||||||
CodecId _codecId;
|
CodecId _codecId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class AudioMeta : public Metadata{
|
class AudioMeta : public Metadata{
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<AudioMeta> Ptr;
|
typedef std::shared_ptr<AudioMeta> Ptr;
|
||||||
|
@ -13,60 +13,56 @@
|
|||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
void RtmpDemuxer::loadMetaData(const AMFValue &val){
|
bool RtmpDemuxer::loadMetaData(const AMFValue &val){
|
||||||
|
bool ret = false;
|
||||||
try {
|
try {
|
||||||
int audiosamplerate = 0;
|
int audiosamplerate = 0;
|
||||||
int audiochannels = 0;
|
int audiochannels = 0;
|
||||||
int audiosamplesize = 0;
|
int audiosamplesize = 0;
|
||||||
const AMFValue *audiocodecid = nullptr;
|
const AMFValue *audiocodecid = nullptr;
|
||||||
const AMFValue *videocodecid = nullptr;
|
const AMFValue *videocodecid = nullptr;
|
||||||
|
|
||||||
val.object_for_each([&](const string &key, const AMFValue &val) {
|
val.object_for_each([&](const string &key, const AMFValue &val) {
|
||||||
if (key == "duration") {
|
if (key == "duration") {
|
||||||
_fDuration = val.as_number();
|
_fDuration = val.as_number();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == "audiosamplerate") {
|
||||||
if(key == "audiosamplerate"){
|
|
||||||
audiosamplerate = val.as_integer();
|
audiosamplerate = val.as_integer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == "audiosamplesize") {
|
||||||
if(key == "audiosamplesize"){
|
|
||||||
audiosamplesize = val.as_integer();
|
audiosamplesize = val.as_integer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == "stereo") {
|
||||||
if(key == "stereo"){
|
|
||||||
audiochannels = val.as_boolean() ? 2 : 1;
|
audiochannels = val.as_boolean() ? 2 : 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == "videocodecid") {
|
||||||
if(key == "videocodecid"){
|
|
||||||
//找到视频
|
//找到视频
|
||||||
videocodecid = &val;
|
videocodecid = &val;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == "audiocodecid") {
|
||||||
if(key == "audiocodecid"){
|
|
||||||
//找到音频
|
//找到音频
|
||||||
audiocodecid = &val;
|
audiocodecid = &val;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (videocodecid) {
|
||||||
if(videocodecid){
|
|
||||||
//有视频
|
//有视频
|
||||||
|
ret = true;
|
||||||
makeVideoTrack(*videocodecid);
|
makeVideoTrack(*videocodecid);
|
||||||
}
|
}
|
||||||
|
if (audiocodecid) {
|
||||||
if(audiocodecid){
|
|
||||||
//有音频
|
//有音频
|
||||||
|
ret = true;
|
||||||
makeAudioTrack(*audiocodecid, audiosamplerate, audiochannels, audiosamplesize);
|
makeAudioTrack(*audiocodecid, audiosamplerate, audiochannels, audiosamplesize);
|
||||||
}
|
}
|
||||||
}catch (std::exception &ex){
|
} catch (std::exception &ex) {
|
||||||
WarnL << ex.what();
|
WarnL << ex.what();
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
|
bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
|
||||||
@ -105,7 +101,7 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) {
|
|||||||
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getVideoTrackByAmf(videoCodec));
|
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getVideoTrackByAmf(videoCodec));
|
||||||
if (_videoTrack) {
|
if (_videoTrack) {
|
||||||
//生成rtmpCodec对象以便解码rtmp
|
//生成rtmpCodec对象以便解码rtmp
|
||||||
_videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack);
|
_videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack, false);
|
||||||
if (_videoRtmpDecoder) {
|
if (_videoRtmpDecoder) {
|
||||||
//设置rtmp解码器代理,生成的frame写入该Track
|
//设置rtmp解码器代理,生成的frame写入该Track
|
||||||
_videoRtmpDecoder->addDelegate(_videoTrack);
|
_videoRtmpDecoder->addDelegate(_videoTrack);
|
||||||
@ -123,7 +119,7 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec,int sample_rate, int
|
|||||||
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit));
|
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit));
|
||||||
if (_audioTrack) {
|
if (_audioTrack) {
|
||||||
//生成rtmpCodec对象以便解码rtmp
|
//生成rtmpCodec对象以便解码rtmp
|
||||||
_audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack);
|
_audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack, false);
|
||||||
if (_audioRtmpDecoder) {
|
if (_audioRtmpDecoder) {
|
||||||
//设置rtmp解码器代理,生成的frame写入该Track
|
//设置rtmp解码器代理,生成的frame写入该Track
|
||||||
_audioRtmpDecoder->addDelegate(_audioTrack);
|
_audioRtmpDecoder->addDelegate(_audioTrack);
|
||||||
|
@ -30,7 +30,7 @@ public:
|
|||||||
RtmpDemuxer() = default;
|
RtmpDemuxer() = default;
|
||||||
virtual ~RtmpDemuxer() = default;
|
virtual ~RtmpDemuxer() = default;
|
||||||
|
|
||||||
void loadMetaData(const AMFValue &metadata);
|
bool loadMetaData(const AMFValue &metadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始解复用
|
* 开始解复用
|
||||||
|
@ -49,8 +49,13 @@ public:
|
|||||||
* 设置metadata
|
* 设置metadata
|
||||||
*/
|
*/
|
||||||
void setMetaData(const AMFValue &metadata) override{
|
void setMetaData(const AMFValue &metadata) override{
|
||||||
_demuxer->loadMetaData(metadata);
|
if(!_demuxer->loadMetaData(metadata)){
|
||||||
RtmpMediaSource::setMetaData(metadata);
|
//该metadata无效,需要重新生成
|
||||||
|
_metadata = metadata;
|
||||||
|
_recreate_metadata = true;
|
||||||
|
}else{
|
||||||
|
RtmpMediaSource::setMetaData(metadata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +143,11 @@ public:
|
|||||||
_muxer->addTrack(track);
|
_muxer->addTrack(track);
|
||||||
track->addDelegate(_muxer);
|
track->addDelegate(_muxer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_recreate_metadata){
|
||||||
|
//需要重新生成metadata
|
||||||
|
Metadata::addTrack(_metadata,track);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,11 +156,19 @@ public:
|
|||||||
void onAllTrackReady() override{
|
void onAllTrackReady() override{
|
||||||
setTrackSource(_muxer);
|
setTrackSource(_muxer);
|
||||||
_all_track_ready = true;
|
_all_track_ready = true;
|
||||||
|
|
||||||
|
if(_recreate_metadata){
|
||||||
|
//需要重新生成metadata
|
||||||
|
RtmpMediaSource::setMetaData(_metadata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtmpDemuxer::Ptr _demuxer;
|
RtmpDemuxer::Ptr _demuxer;
|
||||||
MultiMediaSourceMuxer::Ptr _muxer;
|
MultiMediaSourceMuxer::Ptr _muxer;
|
||||||
|
AMFValue _metadata;
|
||||||
bool _all_track_ready = false;
|
bool _all_track_ready = false;
|
||||||
|
bool _recreate_metadata = false;
|
||||||
};
|
};
|
||||||
} /* namespace mediakit */
|
} /* namespace mediakit */
|
||||||
|
|
||||||
|
@ -23,47 +23,9 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RtmpMuxer::addTrack(const Track::Ptr &track) {
|
void RtmpMuxer::addTrack(const Track::Ptr &track) {
|
||||||
//根据track生产metadata
|
|
||||||
Metadata::Ptr metadata;
|
|
||||||
switch (track->getTrackType()){
|
|
||||||
case TrackVideo:{
|
|
||||||
metadata = std::make_shared<VideoMeta>(dynamic_pointer_cast<VideoTrack>(track));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TrackAudio:{
|
|
||||||
metadata = std::make_shared<AudioMeta>(dynamic_pointer_cast<AudioTrack>(track));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (track->getCodecId()){
|
|
||||||
case CodecG711A:
|
|
||||||
case CodecG711U:{
|
|
||||||
auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
|
|
||||||
if(!audio_track){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (audio_track->getAudioSampleRate() != 8000 ||
|
|
||||||
audio_track->getAudioChannel() != 1 ||
|
|
||||||
audio_track->getAudioSampleBit() != 16) {
|
|
||||||
WarnL << "RTMP只支持8000/1/16规格的G711,目前规格是:"
|
|
||||||
<< audio_track->getAudioSampleRate() << "/"
|
|
||||||
<< audio_track->getAudioChannel() << "/"
|
|
||||||
<< audio_track->getAudioSampleBit()
|
|
||||||
<< ",该音频已被忽略";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default : break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &encoder = _encoder[track->getTrackType()];
|
auto &encoder = _encoder[track->getTrackType()];
|
||||||
//生成rtmp编码器,克隆该Track,防止循环引用
|
//生成rtmp编码器,克隆该Track,防止循环引用
|
||||||
encoder = Factory::getRtmpCodecByTrack(track->clone());
|
encoder = Factory::getRtmpCodecByTrack(track->clone(), true);
|
||||||
if (!encoder) {
|
if (!encoder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -71,10 +33,8 @@ void RtmpMuxer::addTrack(const Track::Ptr &track) {
|
|||||||
//设置rtmp输出环形缓存
|
//设置rtmp输出环形缓存
|
||||||
encoder->setRtmpRing(_rtmpRing);
|
encoder->setRtmpRing(_rtmpRing);
|
||||||
|
|
||||||
//添加其metadata
|
//添加metadata
|
||||||
metadata->getMetadata().object_for_each([&](const std::string &key, const AMFValue &value){
|
Metadata::addTrack(_metadata,track);
|
||||||
_metadata.set(key,value);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtmpMuxer::inputFrame(const Frame::Ptr &frame) {
|
void RtmpMuxer::inputFrame(const Frame::Ptr &frame) {
|
||||||
|
@ -44,6 +44,7 @@ inline void AMFValue::destroy() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AMFValue::init() {
|
inline void AMFValue::init() {
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case AMF_OBJECT:
|
case AMF_OBJECT:
|
||||||
@ -60,14 +61,13 @@ inline void AMFValue::init() {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFValue::AMFValue(AMFType type) :
|
AMFValue::AMFValue(AMFType type) :
|
||||||
_type(type) {
|
_type(type) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AMFValue::~AMFValue() {
|
AMFValue::~AMFValue() {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
@ -78,7 +78,6 @@ AMFValue::AMFValue(const char *s) :
|
|||||||
*_value.string = s;
|
*_value.string = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AMFValue::AMFValue(const std::string &s) :
|
AMFValue::AMFValue(const std::string &s) :
|
||||||
_type(AMF_STRING) {
|
_type(AMF_STRING) {
|
||||||
init();
|
init();
|
||||||
@ -108,15 +107,7 @@ AMFValue::AMFValue(const AMFValue &from) :
|
|||||||
*this = from;
|
*this = from;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFValue::AMFValue(AMFValue &&from) {
|
AMFValue& AMFValue::operator = (const AMFValue &from) {
|
||||||
*this = std::forward<AMFValue>(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
AMFValue& AMFValue::operator =(const AMFValue &from) {
|
|
||||||
return *this = const_cast<AMFValue &&>(from);
|
|
||||||
|
|
||||||
}
|
|
||||||
AMFValue& AMFValue::operator =(AMFValue &&from) {
|
|
||||||
destroy();
|
destroy();
|
||||||
_type = from._type;
|
_type = from._type;
|
||||||
init();
|
init();
|
||||||
@ -144,7 +135,6 @@ AMFValue& AMFValue::operator =(AMFValue &&from) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AMFValue::clear() {
|
void AMFValue::clear() {
|
||||||
@ -236,7 +226,6 @@ string AMFValue::to_string() const{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const AMFValue& AMFValue::operator[](const char *str) const {
|
const AMFValue& AMFValue::operator[](const char *str) const {
|
||||||
if (_type != AMF_OBJECT && _type != AMF_ECMA_ARRAY) {
|
if (_type != AMF_OBJECT && _type != AMF_ECMA_ARRAY) {
|
||||||
throw std::runtime_error("AMF not a object");
|
throw std::runtime_error("AMF not a object");
|
||||||
@ -338,6 +327,7 @@ AMFEncoder & AMFEncoder::operator <<(const char *s) {
|
|||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFEncoder & AMFEncoder::operator <<(const std::string &s) {
|
AMFEncoder & AMFEncoder::operator <<(const std::string &s) {
|
||||||
if (!s.empty()) {
|
if (!s.empty()) {
|
||||||
buf += char(AMF0_STRING);
|
buf += char(AMF0_STRING);
|
||||||
@ -349,18 +339,22 @@ AMFEncoder & AMFEncoder::operator <<(const std::string &s) {
|
|||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFEncoder & AMFEncoder::operator <<(std::nullptr_t) {
|
AMFEncoder & AMFEncoder::operator <<(std::nullptr_t) {
|
||||||
buf += char(AMF0_NULL);
|
buf += char(AMF0_NULL);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFEncoder & AMFEncoder::write_undefined() {
|
AMFEncoder & AMFEncoder::write_undefined() {
|
||||||
buf += char(AMF0_UNDEFINED);
|
buf += char(AMF0_UNDEFINED);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFEncoder & AMFEncoder::operator <<(const int n){
|
AMFEncoder & AMFEncoder::operator <<(const int n){
|
||||||
return (*this) << (double)n;
|
return (*this) << (double)n;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFEncoder & AMFEncoder::operator <<(const double n) {
|
AMFEncoder & AMFEncoder::operator <<(const double n) {
|
||||||
buf += char(AMF0_NUMBER);
|
buf += char(AMF0_NUMBER);
|
||||||
uint64_t encoded = 0;
|
uint64_t encoded = 0;
|
||||||
|
@ -40,6 +40,7 @@ public:
|
|||||||
typedef std::map<std::string, AMFValue> mapType;
|
typedef std::map<std::string, AMFValue> mapType;
|
||||||
typedef std::vector<AMFValue> arrayType;
|
typedef std::vector<AMFValue> arrayType;
|
||||||
|
|
||||||
|
~AMFValue();
|
||||||
AMFValue(AMFType type = AMF_NULL);
|
AMFValue(AMFType type = AMF_NULL);
|
||||||
AMFValue(const char *s);
|
AMFValue(const char *s);
|
||||||
AMFValue(const std::string &s);
|
AMFValue(const std::string &s);
|
||||||
@ -47,10 +48,7 @@ public:
|
|||||||
AMFValue(int i);
|
AMFValue(int i);
|
||||||
AMFValue(bool b);
|
AMFValue(bool b);
|
||||||
AMFValue(const AMFValue &from);
|
AMFValue(const AMFValue &from);
|
||||||
AMFValue(AMFValue &&from);
|
AMFValue &operator = (const AMFValue &from);
|
||||||
AMFValue &operator =(const AMFValue &from);
|
|
||||||
AMFValue &operator =(AMFValue &&from);
|
|
||||||
~AMFValue();
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
AMFType type() const ;
|
AMFType type() const ;
|
||||||
|
Loading…
Reference in New Issue
Block a user