diff --git a/trunk/doc/H.264-AVC-ISO_IEC_14496-15.pdf b/trunk/doc/H.264-AVC-ISO_IEC_14496-15.pdf new file mode 100644 index 000000000..25cbd3a4c Binary files /dev/null and b/trunk/doc/H.264-AVC-ISO_IEC_14496-15.pdf differ diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index 18bd18dbb..20e9b74f3 100644 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -151,6 +151,7 @@ int SrsClient::do_cycle() // find a source to publish. SrsSource* source = SrsSource::find(req->get_stream_url()); srs_assert(source != NULL); + SrsHLS* hls = source->get_hls(); bool enabled_cache = true; conf = config->get_gop_cache(req->vhost); @@ -181,6 +182,7 @@ int SrsClient::do_cycle() } srs_info("start to publish stream %s success", req->stream.c_str()); ret = publish(source, true); + hls->on_unpublish(); source->on_unpublish(); return ret; } @@ -193,6 +195,7 @@ int SrsClient::do_cycle() } srs_info("flash start to publish stream %s success", req->stream.c_str()); ret = publish(source, false); + hls->on_unpublish(); source->on_unpublish(); return ret; } @@ -330,8 +333,15 @@ int SrsClient::publish(SrsSource* source, bool is_fmle) srs_verbose("check publish_refer success."); SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); - SrsHLS* hls = source->get_hls(); + + // notify the hls to prepare when publish start. + if ((ret = hls->on_publish()) != ERROR_SUCCESS) { + srs_error("hls on_publish failed. ret=%d", ret); + return ret; + } + srs_verbose("hls on_publish success."); + while (true) { // switch to other st-threads. st_usleep(0); @@ -366,14 +376,26 @@ int SrsClient::process_publish_message(SrsSource* source, SrsHLS* hls, SrsCommon int ret = ERROR_SUCCESS; // process audio packet - if (msg->header.is_audio() && ((ret = source->on_audio(msg)) != ERROR_SUCCESS)) { - srs_error("process audio message failed. ret=%d", ret); - return ret; + if (msg->header.is_audio()) { + if ((ret = hls->on_audio(msg)) != ERROR_SUCCESS) { + srs_error("hls process audio message failed. ret=%d", ret); + return ret; + } + if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) { + srs_error("source process audio message failed. ret=%d", ret); + return ret; + } } // process video packet - if (msg->header.is_video() && ((ret = source->on_video(msg)) != ERROR_SUCCESS)) { - srs_error("process video message failed. ret=%d", ret); - return ret; + if (msg->header.is_video()) { + if ((ret = hls->on_video(msg)) != ERROR_SUCCESS) { + srs_error("hls process video message failed. ret=%d", ret); + return ret; + } + if ((ret = source->on_video(msg)) != ERROR_SUCCESS) { + srs_error("source process video message failed. ret=%d", ret); + return ret; + } } // process onMetaData @@ -386,8 +408,12 @@ int SrsClient::process_publish_message(SrsSource* source, SrsHLS* hls, SrsCommon SrsPacket* pkt = msg->get_packet(); if (dynamic_cast(pkt)) { SrsOnMetaDataPacket* metadata = dynamic_cast(pkt); + if ((ret = hls->on_meta_data(metadata)) != ERROR_SUCCESS) { + srs_error("hls process onMetaData message failed. ret=%d", ret); + return ret; + } if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) { - srs_error("process onMetaData message failed. ret=%d", ret); + srs_error("source process onMetaData message failed. ret=%d", ret); return ret; } srs_trace("process onMetaData message success."); diff --git a/trunk/src/core/srs_core_codec.cpp b/trunk/src/core/srs_core_codec.cpp index 998904e7f..e3acd242e 100644 --- a/trunk/src/core/srs_core_codec.cpp +++ b/trunk/src/core/srs_core_codec.cpp @@ -23,32 +23,84 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include +#include + SrsCodec::SrsCodec() { + width = 0; + height = 0; + duration = 0; + frame_rate = 0; + video_data_rate = 0; + video_codec_id = 0; + audio_data_rate = 0; + audio_codec_id = 0; + aac_sample_rate = 0; + sample_rate = 0; + sample_size = 0; + audio_channels = 0; + profile = 0; + level = 0; + avc_extra_size = 0; + avc_extra_data = NULL; + aac_extra_size = 0; + aac_extra_data = NULL; + + stream = new SrsStream(); } SrsCodec::~SrsCodec() { + srs_freepa(avc_extra_data); + srs_freepa(aac_extra_data); + + srs_freep(stream); +} + +int SrsCodec::parse_av_codec(bool is_video, int8_t* data, int size) +{ + int ret = ERROR_SUCCESS; + + if (!data || size <= 0) { + return ret; + } + + if ((ret = stream->initialize((char*)data, size)) != ERROR_SUCCESS) { + return ret; + } + + if (is_video) { + if (!stream->require(1)) { + return ret; + } + + int8_t frame_type = stream->read_1bytes(); + int8_t codec_id = frame_type & 0x0f; + frame_type = (frame_type >> 4) & 0x0f; + + video_codec_id = codec_id; + if (codec_id != SrsCodecVideoAVC) { + return ret; + } + + if (!stream->require(4)) { + return ret; + } + int8_t avc_packet_type = stream->read_1bytes(); + int32_t composition_time = stream->read_3bytes(); + + // 5.2.4.1.1 Syntax + if (avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) { + } + } else { + } + + return ret; } bool SrsCodec::video_is_keyframe(int8_t* data, int size) { - // E.4.3.1 VIDEODATA - // Frame Type UB [4] - // Type of video frame. The following values are defined: - // 1 = key frame (for AVC, a seekable frame) - // 2 = inter frame (for AVC, a non-seekable frame) - // 3 = disposable inter frame (H.263 only) - // 4 = generated key frame (reserved for server use only) - // 5 = video info/command frame - // - // AVCPacketType IF CodecID == 7 UI8 - // The following values are defined: - // 0 = AVC sequence header - // 1 = AVC NALU - // 2 = AVC end of sequence (lower level NALU sequence ender is - // not required or supported) - // 2bytes required. if (size < 1) { return false; @@ -57,27 +109,11 @@ bool SrsCodec::video_is_keyframe(int8_t* data, int size) char frame_type = *(char*)data; frame_type = (frame_type >> 4) & 0x0F; - return frame_type == 1; + return frame_type == SrsCodecVideoAVCFrameKeyFrame; } bool SrsCodec::video_is_sequence_header(int8_t* data, int size) { - // E.4.3.1 VIDEODATA - // Frame Type UB [4] - // Type of video frame. The following values are defined: - // 1 = key frame (for AVC, a seekable frame) - // 2 = inter frame (for AVC, a non-seekable frame) - // 3 = disposable inter frame (H.263 only) - // 4 = generated key frame (reserved for server use only) - // 5 = video info/command frame - // - // AVCPacketType IF CodecID == 7 UI8 - // The following values are defined: - // 0 = AVC sequence header - // 1 = AVC NALU - // 2 = AVC end of sequence (lower level NALU sequence ender is - // not required or supported) - // sequence header only for h264 if (!video_is_h264(data, size)) { return false; @@ -93,16 +129,12 @@ bool SrsCodec::video_is_sequence_header(int8_t* data, int size) char avc_packet_type = *(char*)(data + 1); - return frame_type == 1 && avc_packet_type == 0; + return frame_type == SrsCodecVideoAVCFrameKeyFrame + && avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader; } bool SrsCodec::audio_is_sequence_header(int8_t* data, int size) { - // AACPacketType IF SoundFormat == 10 UI8 - // The following values are defined: - // 0 = AAC sequence header - // 1 = AAC raw - // sequence header only for aac if (!audio_is_aac(data, size)) { return false; @@ -115,21 +147,11 @@ bool SrsCodec::audio_is_sequence_header(int8_t* data, int size) char aac_packet_type = *(char*)(data + 1); - return aac_packet_type == 0; + return aac_packet_type == SrsCodecAudioTypeSequenceHeader; } bool SrsCodec::video_is_h264(int8_t* data, int size) { - // E.4.3.1 VIDEODATA - // CodecID UB [4] - // Codec Identifier. The following values are defined: - // 2 = Sorenson H.263 - // 3 = Screen video - // 4 = On2 VP6 - // 5 = On2 VP6 with alpha channel - // 6 = Screen video version 2 - // 7 = AVC - // 1bytes required. if (size < 1) { return false; @@ -138,31 +160,11 @@ bool SrsCodec::video_is_h264(int8_t* data, int size) char codec_id = *(char*)data; codec_id = codec_id & 0x0F; - return codec_id == 7; + return codec_id == SrsCodecVideoAVC; } bool SrsCodec::audio_is_aac(int8_t* data, int size) { - // SoundFormat UB [4] - // Format of SoundData. The following values are defined: - // 0 = Linear PCM, platform endian - // 1 = ADPCM - // 2 = MP3 - // 3 = Linear PCM, little endian - // 4 = Nellymoser 16 kHz mono - // 5 = Nellymoser 8 kHz mono - // 6 = Nellymoser - // 7 = G.711 A-law logarithmic PCM - // 8 = G.711 mu-law logarithmic PCM - // 9 = reserved - // 10 = AAC - // 11 = Speex - // 14 = MP3 8 kHz - // 15 = Device-specific sound - // Formats 7, 8, 14, and 15 are reserved. - // AAC is supported in Flash Player 9,0,115,0 and higher. - // Speex is supported in Flash Player 10 and higher. - // 1bytes required. if (size < 1) { return false; @@ -171,5 +173,5 @@ bool SrsCodec::audio_is_aac(int8_t* data, int size) char sound_format = *(char*)data; sound_format = (sound_format >> 4) & 0x0F; - return sound_format == 10; + return sound_format == SrsCodecAudioAAC; } diff --git a/trunk/src/core/srs_core_codec.hpp b/trunk/src/core/srs_core_codec.hpp index 2e24fd871..6d233e7be 100644 --- a/trunk/src/core/srs_core_codec.hpp +++ b/trunk/src/core/srs_core_codec.hpp @@ -30,36 +30,170 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +class SrsStream; + +// E.4.3.1 VIDEODATA +// CodecID UB [4] +// Codec Identifier. The following values are defined: +// 2 = Sorenson H.263 +// 3 = Screen video +// 4 = On2 VP6 +// 5 = On2 VP6 with alpha channel +// 6 = Screen video version 2 +// 7 = AVC +enum SrsCodecVideo +{ + SrsCodecVideoSorensonH263 = 2, + SrsCodecVideoScreenVideo = 3, + SrsCodecVideoOn2VP6 = 4, + SrsCodecVideoOn2VP6WithAlphaChannel = 5, + SrsCodecVideoScreenVideoVersion2 = 6, + SrsCodecVideoAVC = 7, +}; + +// E.4.3.1 VIDEODATA +// Frame Type UB [4] +// Type of video frame. The following values are defined: +// 1 = key frame (for AVC, a seekable frame) +// 2 = inter frame (for AVC, a non-seekable frame) +// 3 = disposable inter frame (H.263 only) +// 4 = generated key frame (reserved for server use only) +// 5 = video info/command frame +enum SrsCodecVideoAVCFrame +{ + SrsCodecVideoAVCFrameKeyFrame = 1, + SrsCodecVideoAVCFrameInterFrame = 2, + SrsCodecVideoAVCFrameDisposableInterFrame = 3, + SrsCodecVideoAVCFrameGeneratedKeyFrame = 4, + SrsCodecVideoAVCFrameVideoInfoFrame = 5, +}; + +// AVCPacketType IF CodecID == 7 UI8 +// The following values are defined: +// 0 = AVC sequence header +// 1 = AVC NALU +// 2 = AVC end of sequence (lower level NALU sequence ender is +// not required or supported) +enum SrsCodecVideoAVCType +{ + SrsCodecVideoAVCTypeSequenceHeader = 0, + SrsCodecVideoAVCTypeNALU = 1, + SrsCodecVideoAVCTypeSequenceHeaderEOF = 2, +}; + +// SoundFormat UB [4] +// Format of SoundData. The following values are defined: +// 0 = Linear PCM, platform endian +// 1 = ADPCM +// 2 = MP3 +// 3 = Linear PCM, little endian +// 4 = Nellymoser 16 kHz mono +// 5 = Nellymoser 8 kHz mono +// 6 = Nellymoser +// 7 = G.711 A-law logarithmic PCM +// 8 = G.711 mu-law logarithmic PCM +// 9 = reserved +// 10 = AAC +// 11 = Speex +// 14 = MP3 8 kHz +// 15 = Device-specific sound +// Formats 7, 8, 14, and 15 are reserved. +// AAC is supported in Flash Player 9,0,115,0 and higher. +// Speex is supported in Flash Player 10 and higher. +enum SrsCodecAudio +{ + SrsCodecAudioLinearPCMPlatformEndian = 0, + SrsCodecAudioADPCM = 1, + SrsCodecAudioMP3 = 2, + SrsCodecAudioLinearPCMLittleEndian = 3, + SrsCodecAudioNellymoser16kHzMono = 4, + SrsCodecAudioNellymoser8kHzMono = 5, + SrsCodecAudioNellymoser = 6, + SrsCodecAudioReservedG711AlawLogarithmicPCM = 7, + SrsCodecAudioReservedG711MuLawLogarithmicPCM = 8, + SrsCodecAudioReserved = 9, + SrsCodecAudioAAC = 10, + SrsCodecAudioSpeex = 11, + SrsCodecAudioReservedMP3_8kHz = 14, + SrsCodecAudioReservedDeviceSpecificSound = 15, +}; + +// AACPacketType IF SoundFormat == 10 UI8 +// The following values are defined: +// 0 = AAC sequence header +// 1 = AAC raw +enum SrsCodecAudioType +{ + SrsCodecAudioTypeSequenceHeader = 0, + SrsCodecAudioTypeRawData = 1, +}; + /** * Annex E. The FLV File Format */ class SrsCodec { +private: + SrsStream* stream; +public: + /** + * video specified + */ + int width; + int height; + int duration; + int frame_rate; + // @see: SrsCodecVideo + int video_codec_id; + int video_data_rate; // in bps + u_int8_t profile; // profile_idc, page 45. + u_int8_t level; // level_idc, page 45. + /** + * audio specified + */ + int audio_codec_id; + int audio_data_rate; // in bps + int aac_sample_rate; + int sample_rate; /* 5512, 11025, 22050, 44100 */ + int sample_size; /* 1=8bit, 2=16bit */ + int audio_channels; /* 1, 2 */ + // the avc extra data, the AVC sequence header, + // without the flv codec header + int avc_extra_size; + char* avc_extra_data; + // the aac extra data, the AAC sequence header, + // without the flv codec header + int aac_extra_size; + char* aac_extra_data; public: SrsCodec(); virtual ~SrsCodec(); +// the following function used for hls to build the codec info. +public: + virtual int parse_av_codec(bool is_video, int8_t* data, int size); +// the following function used to finger out the flv/rtmp packet detail. public: /** * only check the frame_type, not check the codec type. */ - virtual bool video_is_keyframe(int8_t* data, int size); + static bool video_is_keyframe(int8_t* data, int size); /** * check codec h264, keyframe, sequence header */ - virtual bool video_is_sequence_header(int8_t* data, int size); + static bool video_is_sequence_header(int8_t* data, int size); /** * check codec aac, sequence header */ - virtual bool audio_is_sequence_header(int8_t* data, int size); + static bool audio_is_sequence_header(int8_t* data, int size); /** * check codec h264. */ - virtual bool video_is_h264(int8_t* data, int size); + static bool video_is_h264(int8_t* data, int size); private: /** * check codec aac. */ - virtual bool audio_is_aac(int8_t* data, int size); + static bool audio_is_aac(int8_t* data, int size); }; #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_hls.cpp b/trunk/src/core/srs_core_hls.cpp index d7ed52c52..451377600 100644 --- a/trunk/src/core/srs_core_hls.cpp +++ b/trunk/src/core/srs_core_hls.cpp @@ -23,11 +23,117 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include +#include +#include +#include + SrsHLS::SrsHLS() { + codec = new SrsCodec(); } SrsHLS::~SrsHLS() { + srs_freep(codec); +} + +int SrsHLS::on_publish() +{ + int ret = ERROR_SUCCESS; + return ret; +} + +void SrsHLS::on_unpublish() +{ +} + +int SrsHLS::on_meta_data(SrsOnMetaDataPacket* metadata) +{ + int ret = ERROR_SUCCESS; + + if (!metadata || !metadata->metadata) { + return ret; + } + + SrsAmf0Object* obj = metadata->metadata; + if (obj->size() <= 0) { + return ret; + } + + // finger out the codec info from metadata if possible. + SrsAmf0Any* prop = NULL; + + if ((prop = obj->get_property("duration")) != NULL && prop->is_number()) { + codec->duration = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("width")) != NULL && prop->is_number()) { + codec->width = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("height")) != NULL && prop->is_number()) { + codec->height = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("framerate")) != NULL && prop->is_number()) { + codec->frame_rate = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("videocodecid")) != NULL && prop->is_number()) { + codec->video_codec_id = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("videodatarate")) != NULL && prop->is_number()) { + codec->video_data_rate = (int)(1000 * srs_amf0_convert(prop)->value); + } + + if ((prop = obj->get_property("audiocodecid")) != NULL && prop->is_number()) { + codec->audio_codec_id = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("audiodatarate")) != NULL && prop->is_number()) { + codec->audio_data_rate = (int)(1000 * srs_amf0_convert(prop)->value); + } + if ((prop = obj->get_property("audiosamplerate")) != NULL && prop->is_number()) { + codec->sample_rate = (int)srs_amf0_convert(prop)->value; + } + if ((prop = obj->get_property("audiosamplesize")) != NULL && prop->is_number()) { + codec->sample_size = (int)srs_amf0_convert(prop)->value; + if (codec->sample_size == 16) { + codec->sample_size = 2; + } else { + codec->sample_size = 1; + } + } + if ((prop = obj->get_property("stereo")) != NULL && prop->is_number()) { + if (srs_amf0_convert(prop)->value) { + codec->audio_channels = 2; + } else { + codec->audio_channels = 1; + } + } + + return ret; +} + +int SrsHLS::on_audio(SrsCommonMessage* audio) +{ + int ret = ERROR_SUCCESS; + + if ((ret = codec->parse_av_codec(false, audio->payload, audio->size)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsHLS::on_video(SrsCommonMessage* video) +{ + int ret = ERROR_SUCCESS; + + if ((ret = codec->parse_av_codec(true, video->payload, video->size)) != ERROR_SUCCESS) { + return ret; + } + + if (codec->video_codec_id != SrsCodecVideoAVC) { + return ret; + } + + return ret; } diff --git a/trunk/src/core/srs_core_hls.hpp b/trunk/src/core/srs_core_hls.hpp index a9e97f87b..dab871027 100644 --- a/trunk/src/core/srs_core_hls.hpp +++ b/trunk/src/core/srs_core_hls.hpp @@ -29,11 +29,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include +class SrsOnMetaDataPacket; +class SrsCommonMessage; +class SrsCodec; + class SrsHLS { +private: + SrsCodec* codec; public: SrsHLS(); virtual ~SrsHLS(); +public: + virtual int on_publish(); + virtual void on_unpublish(); + virtual int on_meta_data(SrsOnMetaDataPacket* metadata); + virtual int on_audio(SrsCommonMessage* audio); + virtual int on_video(SrsCommonMessage* video); }; #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp index 93f5b9694..3400a5878 100644 --- a/trunk/src/core/srs_core_source.cpp +++ b/trunk/src/core/srs_core_source.cpp @@ -53,13 +53,11 @@ SrsConsumer::SrsConsumer(SrsSource* _source) source = _source; last_pkt_correct_time = last_pkt_time = 0; paused = false; - codec = new SrsCodec(); } SrsConsumer::~SrsConsumer() { - clear(); - srs_freep(codec); + clear(); source->on_consumer_destroy(this); } @@ -141,7 +139,7 @@ void SrsConsumer::shrink() SrsSharedPtrMessage* msg = *it; if (msg->header.is_video()) { has_video = true; - if (codec->video_is_keyframe(msg->payload, msg->size)) { + if (SrsCodec::video_is_keyframe(msg->payload, msg->size)) { iframe = it; frame_to_remove = i + 1; } @@ -240,7 +238,6 @@ void SrsConsumer::clear() SrsSource::SrsSource(std::string _stream_url) { stream_url = _stream_url; - codec = new SrsCodec(); hls = new SrsHLS(); cache_metadata = cache_sh_video = cache_sh_audio = NULL; @@ -266,7 +263,6 @@ SrsSource::~SrsSource() srs_freep(cache_sh_video); srs_freep(cache_sh_audio); - srs_freep(codec); srs_freep(hls); } @@ -364,7 +360,7 @@ int SrsSource::on_audio(SrsCommonMessage* audio) srs_info("dispatch audio success."); // cache the sequence header if h264 - if (codec->audio_is_sequence_header(msg->payload, msg->size)) { + if (SrsCodec::audio_is_sequence_header(msg->payload, msg->size)) { srs_freep(cache_sh_audio); cache_sh_audio = msg->copy(); srs_trace("update audio sequence header success. size=%d", msg->header.payload_length); @@ -409,7 +405,7 @@ int SrsSource::on_video(SrsCommonMessage* video) srs_info("dispatch video success."); // cache the sequence header if h264 - if (codec->video_is_sequence_header(msg->payload, msg->size)) { + if (SrsCodec::video_is_sequence_header(msg->payload, msg->size)) { srs_freep(cache_sh_video); cache_sh_video = msg->copy(); srs_trace("update video sequence header success. size=%d", msg->header.payload_length); @@ -521,7 +517,7 @@ int SrsSource::cache_last_gop(SrsSharedPtrMessage* msg) } // clear gop cache when got key frame - if (msg->header.is_video() && codec->video_is_keyframe(msg->payload, msg->size)) { + if (msg->header.is_video() && SrsCodec::video_is_keyframe(msg->payload, msg->size)) { srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", cached_video_count, (int)gop_cache.size()); diff --git a/trunk/src/core/srs_core_source.hpp b/trunk/src/core/srs_core_source.hpp index b512a4f64..5060f054a 100644 --- a/trunk/src/core/srs_core_source.hpp +++ b/trunk/src/core/srs_core_source.hpp @@ -34,7 +34,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -class SrsCodec; class SrsSource; class SrsCommonMessage; class SrsOnMetaDataPacket; @@ -52,7 +51,6 @@ private: SrsSource* source; std::vector msgs; bool paused; - SrsCodec* codec; public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); @@ -108,7 +106,6 @@ public: static SrsSource* find(std::string stream_url); private: SrsHLS* hls; - SrsCodec* codec; std::string stream_url; std::vector consumers; // gop cache for client fast startup. diff --git a/trunk/src/core/srs_core_stream.cpp b/trunk/src/core/srs_core_stream.cpp index 4b8930315..ae7e0a919 100644 --- a/trunk/src/core/srs_core_stream.cpp +++ b/trunk/src/core/srs_core_stream.cpp @@ -106,6 +106,19 @@ int16_t SrsStream::read_2bytes() return value; } +int32_t SrsStream::read_3bytes() +{ + srs_assert(require(3)); + + int32_t value = 0x00; + pp = (char*)&value; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + return value; +} + int32_t SrsStream::read_4bytes() { srs_assert(require(4)); diff --git a/trunk/src/core/srs_core_stream.hpp b/trunk/src/core/srs_core_stream.hpp index 9c0ee0769..2d4f437b0 100644 --- a/trunk/src/core/srs_core_stream.hpp +++ b/trunk/src/core/srs_core_stream.hpp @@ -84,6 +84,10 @@ public: */ virtual int16_t read_2bytes(); /** + * get 3bytes int from stream. + */ + virtual int32_t read_3bytes(); + /** * get 4bytes int from stream. */ virtual int32_t read_4bytes();