diff --git a/README.md b/README.md index d83cca4da..aeae5b7d6 100755 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ url: rtmp://127.0.0.1:1935/live/livestream * nginx v1.5.0: 139524 lines
### History -* v0.2, 2013-10-23, v0.2 released. 10125 lines. +* v0.3, 2013-10-27, support cache last gop for client fast startup. +* v0.2, 2013-10-25, v0.2 released. 10125 lines. * v0.2, 2013-10-25, support flash publish. * v0.2, 2013-10-25, support h264/avc codec by rtmp complex handshake(SrsComplexHandshake). * v0.2, 2013-10-24, support time jitter detect and correct algorithm(SrsConsumer::jitter_correct). diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index a563f8afd..7cceb2367 100755 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -145,7 +145,9 @@ int SrsClient::do_cycle() return ret; } srs_info("start to publish stream %s success", req->stream.c_str()); - return streaming_publish(source, true); + ret = streaming_publish(source, true); + source->on_unpublish(); + return ret; } case SrsClientFlashPublish: { srs_verbose("flash start to publish stream %s.", req->stream.c_str()); @@ -155,7 +157,9 @@ int SrsClient::do_cycle() return ret; } srs_info("flash start to publish stream %s success", req->stream.c_str()); - return streaming_publish(source, false); + ret = streaming_publish(source, false); + source->on_unpublish(); + return ret; } default: { ret = ERROR_SYSTEM_CLIENT_INVALID; diff --git a/trunk/src/core/srs_core_codec.cpp b/trunk/src/core/srs_core_codec.cpp index bd445d740..c2d0b0dfd 100755 --- a/trunk/src/core/srs_core_codec.cpp +++ b/trunk/src/core/srs_core_codec.cpp @@ -31,6 +31,35 @@ SrsCodec::~SrsCodec() { } +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; + } + + char frame_type = *(char*)data; + frame_type = (frame_type >> 4) & 0x0F; + + return frame_type == 1; +} + bool SrsCodec::video_is_sequence_header(int8_t* data, int size) { // E.4.3.1 VIDEODATA diff --git a/trunk/src/core/srs_core_codec.hpp b/trunk/src/core/srs_core_codec.hpp index 49e8cc2c8..ae73e6302 100755 --- a/trunk/src/core/srs_core_codec.hpp +++ b/trunk/src/core/srs_core_codec.hpp @@ -40,10 +40,26 @@ public: SrsCodec(); virtual ~SrsCodec(); public: + /** + * only check the frame_type, not check the codec type. + */ + virtual 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); + /** + * check codec aac, sequence header + */ virtual bool audio_is_sequence_header(int8_t* data, int size); -private: + /** + * check codec h264. + */ virtual bool video_is_h264(int8_t* data, int size); +private: + /** + * check codec aac. + */ virtual bool audio_is_aac(int8_t* data, int size); }; diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp index dada68bb8..22a4da2eb 100755 --- a/trunk/src/core/srs_core_source.cpp +++ b/trunk/src/core/srs_core_source.cpp @@ -64,6 +64,11 @@ SrsConsumer::~SrsConsumer() source->on_consumer_destroy(this); } +int SrsConsumer::get_time() +{ + return (int)last_pkt_correct_time; +} + int SrsConsumer::enqueue(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; @@ -144,10 +149,12 @@ int SrsConsumer::jitter_correct(SrsSharedPtrMessage* msg) SrsSource::SrsSource(std::string _stream_url) { stream_url = _stream_url; - cache_metadata = NULL; - cache_sh_video = NULL; - cache_sh_audio = NULL; codec = new SrsCodec(); + + cache_metadata = cache_sh_video = cache_sh_audio = NULL; + + cached_video_count = 0; + enable_gop_cache = true; } SrsSource::~SrsSource() @@ -159,6 +166,8 @@ SrsSource::~SrsSource() } consumers.clear(); + clear_gop_cache(); + srs_freep(cache_metadata); srs_freep(cache_sh_video); srs_freep(cache_sh_audio); @@ -246,8 +255,16 @@ int SrsSource::on_audio(SrsCommonMessage* audio) if (codec->audio_is_sequence_header(msg->payload, msg->size)) { srs_freep(cache_sh_audio); cache_sh_audio = msg->copy(); + return ret; } + // cache the last gop packets + if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { + srs_error("shrink gop cache failed. ret=%d", ret); + return ret; + } + srs_verbose("cache gop success."); + return ret; } @@ -282,7 +299,15 @@ int SrsSource::on_video(SrsCommonMessage* video) if (codec->video_is_sequence_header(msg->payload, msg->size)) { srs_freep(cache_sh_video); cache_sh_video = msg->copy(); + return ret; } + + // cache the last gop packets + if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { + srs_error("shrink gop cache failed. ret=%d", ret); + return ret; + } + srs_verbose("cache gop success."); return ret; } @@ -312,6 +337,16 @@ int SrsSource::on_video(SrsCommonMessage* video) } srs_info("dispatch audio sequence header success"); + std::vector::iterator it; + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + if ((ret = consumer->enqueue(msg->copy())) != ERROR_SUCCESS) { + srs_error("dispatch cached gop failed. ret=%d", ret); + return ret; + } + } + srs_trace("dispatch cached gop success. count=%d, duration=%d", (int)gop_cache.size(), consumer->get_time()); + return ret; } @@ -325,3 +360,58 @@ void SrsSource::on_consumer_destroy(SrsConsumer* consumer) srs_info("handle consumer destroy success."); } +void SrsSource::on_unpublish() +{ + clear_gop_cache(); + srs_trace("clear cache when unpublish."); +} + +int SrsSource::cache_last_gop(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (!enable_gop_cache) { + srs_verbose("gop cache is disabled."); + return ret; + } + + // got video, update the video count if acceptable + if (msg->header.is_video()) { + cached_video_count++; + } + + // no acceptable video or pure audio, disable the cache. + if (cached_video_count == 0) { + srs_verbose("ignore any frame util got a h264 video frame."); + return ret; + } + + // clear gop cache when got key frame + if (msg->header.is_video() && codec->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()); + + clear_gop_cache(); + + // curent msg is video frame, so we set to 1. + cached_video_count = 1; + } + + // cache the frame. + gop_cache.push_back(msg->copy()); + + return ret; +} + +void SrsSource::clear_gop_cache() +{ + std::vector::iterator it; + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + gop_cache.clear(); + + cached_video_count = 0; +} + diff --git a/trunk/src/core/srs_core_source.hpp b/trunk/src/core/srs_core_source.hpp index dedcaa5f1..b2737aeec 100755 --- a/trunk/src/core/srs_core_source.hpp +++ b/trunk/src/core/srs_core_source.hpp @@ -54,6 +54,10 @@ public: SrsConsumer(SrsSource* _source); virtual ~SrsConsumer(); public: + /** + * get current client time, the last packet time. + */ + virtual int get_time(); /** * enqueue an shared ptr message. */ @@ -92,6 +96,22 @@ private: SrsCodec* codec; std::string stream_url; std::vector consumers; +// gop cache for client fast startup. +private: + /** + * if disabled the gop cache, + * the client will wait for the next keyframe for h264, + * and will be black-screen. + */ + bool enable_gop_cache; + /** + * the video frame count, avoid cache for pure audio stream. + */ + int cached_video_count; + /** + * cached gop. + */ + std::vector gop_cache; private: SrsSharedPtrMessage* cache_metadata; // the cached video sequence header. @@ -108,6 +128,15 @@ public: public: virtual int create_consumer(SrsConsumer*& consumer); virtual void on_consumer_destroy(SrsConsumer* consumer); + virtual void on_unpublish(); +private: + /** + * only for h264 codec + * 1. cache the gop when got h264 video packet. + * 2. clear gop when got keyframe. + */ + virtual int cache_last_gop(SrsSharedPtrMessage* msg); + virtual void clear_gop_cache(); }; #endif \ No newline at end of file