正在显示
6 个修改的文件
包含
176 行增加
和
7 行删除
| @@ -45,7 +45,8 @@ url: rtmp://127.0.0.1:1935/live/livestream | @@ -45,7 +45,8 @@ url: rtmp://127.0.0.1:1935/live/livestream | ||
| 45 | * nginx v1.5.0: 139524 lines <br/> | 45 | * nginx v1.5.0: 139524 lines <br/> |
| 46 | 46 | ||
| 47 | ### History | 47 | ### History |
| 48 | -* v0.2, 2013-10-23, v0.2 released. 10125 lines. | 48 | +* v0.3, 2013-10-27, support cache last gop for client fast startup. |
| 49 | +* v0.2, 2013-10-25, v0.2 released. 10125 lines. | ||
| 49 | * v0.2, 2013-10-25, support flash publish. | 50 | * v0.2, 2013-10-25, support flash publish. |
| 50 | * v0.2, 2013-10-25, support h264/avc codec by rtmp complex handshake(SrsComplexHandshake). | 51 | * v0.2, 2013-10-25, support h264/avc codec by rtmp complex handshake(SrsComplexHandshake). |
| 51 | * v0.2, 2013-10-24, support time jitter detect and correct algorithm(SrsConsumer::jitter_correct). | 52 | * v0.2, 2013-10-24, support time jitter detect and correct algorithm(SrsConsumer::jitter_correct). |
| @@ -145,7 +145,9 @@ int SrsClient::do_cycle() | @@ -145,7 +145,9 @@ int SrsClient::do_cycle() | ||
| 145 | return ret; | 145 | return ret; |
| 146 | } | 146 | } |
| 147 | srs_info("start to publish stream %s success", req->stream.c_str()); | 147 | srs_info("start to publish stream %s success", req->stream.c_str()); |
| 148 | - return streaming_publish(source, true); | 148 | + ret = streaming_publish(source, true); |
| 149 | + source->on_unpublish(); | ||
| 150 | + return ret; | ||
| 149 | } | 151 | } |
| 150 | case SrsClientFlashPublish: { | 152 | case SrsClientFlashPublish: { |
| 151 | srs_verbose("flash start to publish stream %s.", req->stream.c_str()); | 153 | srs_verbose("flash start to publish stream %s.", req->stream.c_str()); |
| @@ -155,7 +157,9 @@ int SrsClient::do_cycle() | @@ -155,7 +157,9 @@ int SrsClient::do_cycle() | ||
| 155 | return ret; | 157 | return ret; |
| 156 | } | 158 | } |
| 157 | srs_info("flash start to publish stream %s success", req->stream.c_str()); | 159 | srs_info("flash start to publish stream %s success", req->stream.c_str()); |
| 158 | - return streaming_publish(source, false); | 160 | + ret = streaming_publish(source, false); |
| 161 | + source->on_unpublish(); | ||
| 162 | + return ret; | ||
| 159 | } | 163 | } |
| 160 | default: { | 164 | default: { |
| 161 | ret = ERROR_SYSTEM_CLIENT_INVALID; | 165 | ret = ERROR_SYSTEM_CLIENT_INVALID; |
| @@ -31,6 +31,35 @@ SrsCodec::~SrsCodec() | @@ -31,6 +31,35 @@ SrsCodec::~SrsCodec() | ||
| 31 | { | 31 | { |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | +bool SrsCodec::video_is_keyframe(int8_t* data, int size) | ||
| 35 | +{ | ||
| 36 | + // E.4.3.1 VIDEODATA | ||
| 37 | + // Frame Type UB [4] | ||
| 38 | + // Type of video frame. The following values are defined: | ||
| 39 | + // 1 = key frame (for AVC, a seekable frame) | ||
| 40 | + // 2 = inter frame (for AVC, a non-seekable frame) | ||
| 41 | + // 3 = disposable inter frame (H.263 only) | ||
| 42 | + // 4 = generated key frame (reserved for server use only) | ||
| 43 | + // 5 = video info/command frame | ||
| 44 | + // | ||
| 45 | + // AVCPacketType IF CodecID == 7 UI8 | ||
| 46 | + // The following values are defined: | ||
| 47 | + // 0 = AVC sequence header | ||
| 48 | + // 1 = AVC NALU | ||
| 49 | + // 2 = AVC end of sequence (lower level NALU sequence ender is | ||
| 50 | + // not required or supported) | ||
| 51 | + | ||
| 52 | + // 2bytes required. | ||
| 53 | + if (size < 1) { | ||
| 54 | + return false; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + char frame_type = *(char*)data; | ||
| 58 | + frame_type = (frame_type >> 4) & 0x0F; | ||
| 59 | + | ||
| 60 | + return frame_type == 1; | ||
| 61 | +} | ||
| 62 | + | ||
| 34 | bool SrsCodec::video_is_sequence_header(int8_t* data, int size) | 63 | bool SrsCodec::video_is_sequence_header(int8_t* data, int size) |
| 35 | { | 64 | { |
| 36 | // E.4.3.1 VIDEODATA | 65 | // E.4.3.1 VIDEODATA |
| @@ -40,10 +40,26 @@ public: | @@ -40,10 +40,26 @@ public: | ||
| 40 | SrsCodec(); | 40 | SrsCodec(); |
| 41 | virtual ~SrsCodec(); | 41 | virtual ~SrsCodec(); |
| 42 | public: | 42 | public: |
| 43 | + /** | ||
| 44 | + * only check the frame_type, not check the codec type. | ||
| 45 | + */ | ||
| 46 | + virtual bool video_is_keyframe(int8_t* data, int size); | ||
| 47 | + /** | ||
| 48 | + * check codec h264, keyframe, sequence header | ||
| 49 | + */ | ||
| 43 | virtual bool video_is_sequence_header(int8_t* data, int size); | 50 | virtual bool video_is_sequence_header(int8_t* data, int size); |
| 51 | + /** | ||
| 52 | + * check codec aac, sequence header | ||
| 53 | + */ | ||
| 44 | virtual bool audio_is_sequence_header(int8_t* data, int size); | 54 | virtual bool audio_is_sequence_header(int8_t* data, int size); |
| 45 | -private: | 55 | + /** |
| 56 | + * check codec h264. | ||
| 57 | + */ | ||
| 46 | virtual bool video_is_h264(int8_t* data, int size); | 58 | virtual bool video_is_h264(int8_t* data, int size); |
| 59 | +private: | ||
| 60 | + /** | ||
| 61 | + * check codec aac. | ||
| 62 | + */ | ||
| 47 | virtual bool audio_is_aac(int8_t* data, int size); | 63 | virtual bool audio_is_aac(int8_t* data, int size); |
| 48 | }; | 64 | }; |
| 49 | 65 |
| @@ -64,6 +64,11 @@ SrsConsumer::~SrsConsumer() | @@ -64,6 +64,11 @@ SrsConsumer::~SrsConsumer() | ||
| 64 | source->on_consumer_destroy(this); | 64 | source->on_consumer_destroy(this); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | +int SrsConsumer::get_time() | ||
| 68 | +{ | ||
| 69 | + return (int)last_pkt_correct_time; | ||
| 70 | +} | ||
| 71 | + | ||
| 67 | int SrsConsumer::enqueue(SrsSharedPtrMessage* msg) | 72 | int SrsConsumer::enqueue(SrsSharedPtrMessage* msg) |
| 68 | { | 73 | { |
| 69 | int ret = ERROR_SUCCESS; | 74 | int ret = ERROR_SUCCESS; |
| @@ -144,10 +149,12 @@ int SrsConsumer::jitter_correct(SrsSharedPtrMessage* msg) | @@ -144,10 +149,12 @@ int SrsConsumer::jitter_correct(SrsSharedPtrMessage* msg) | ||
| 144 | SrsSource::SrsSource(std::string _stream_url) | 149 | SrsSource::SrsSource(std::string _stream_url) |
| 145 | { | 150 | { |
| 146 | stream_url = _stream_url; | 151 | stream_url = _stream_url; |
| 147 | - cache_metadata = NULL; | ||
| 148 | - cache_sh_video = NULL; | ||
| 149 | - cache_sh_audio = NULL; | ||
| 150 | codec = new SrsCodec(); | 152 | codec = new SrsCodec(); |
| 153 | + | ||
| 154 | + cache_metadata = cache_sh_video = cache_sh_audio = NULL; | ||
| 155 | + | ||
| 156 | + cached_video_count = 0; | ||
| 157 | + enable_gop_cache = true; | ||
| 151 | } | 158 | } |
| 152 | 159 | ||
| 153 | SrsSource::~SrsSource() | 160 | SrsSource::~SrsSource() |
| @@ -159,6 +166,8 @@ SrsSource::~SrsSource() | @@ -159,6 +166,8 @@ SrsSource::~SrsSource() | ||
| 159 | } | 166 | } |
| 160 | consumers.clear(); | 167 | consumers.clear(); |
| 161 | 168 | ||
| 169 | + clear_gop_cache(); | ||
| 170 | + | ||
| 162 | srs_freep(cache_metadata); | 171 | srs_freep(cache_metadata); |
| 163 | srs_freep(cache_sh_video); | 172 | srs_freep(cache_sh_video); |
| 164 | srs_freep(cache_sh_audio); | 173 | srs_freep(cache_sh_audio); |
| @@ -246,8 +255,16 @@ int SrsSource::on_audio(SrsCommonMessage* audio) | @@ -246,8 +255,16 @@ int SrsSource::on_audio(SrsCommonMessage* audio) | ||
| 246 | if (codec->audio_is_sequence_header(msg->payload, msg->size)) { | 255 | if (codec->audio_is_sequence_header(msg->payload, msg->size)) { |
| 247 | srs_freep(cache_sh_audio); | 256 | srs_freep(cache_sh_audio); |
| 248 | cache_sh_audio = msg->copy(); | 257 | cache_sh_audio = msg->copy(); |
| 258 | + return ret; | ||
| 249 | } | 259 | } |
| 250 | 260 | ||
| 261 | + // cache the last gop packets | ||
| 262 | + if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { | ||
| 263 | + srs_error("shrink gop cache failed. ret=%d", ret); | ||
| 264 | + return ret; | ||
| 265 | + } | ||
| 266 | + srs_verbose("cache gop success."); | ||
| 267 | + | ||
| 251 | return ret; | 268 | return ret; |
| 252 | } | 269 | } |
| 253 | 270 | ||
| @@ -282,7 +299,15 @@ int SrsSource::on_video(SrsCommonMessage* video) | @@ -282,7 +299,15 @@ int SrsSource::on_video(SrsCommonMessage* video) | ||
| 282 | if (codec->video_is_sequence_header(msg->payload, msg->size)) { | 299 | if (codec->video_is_sequence_header(msg->payload, msg->size)) { |
| 283 | srs_freep(cache_sh_video); | 300 | srs_freep(cache_sh_video); |
| 284 | cache_sh_video = msg->copy(); | 301 | cache_sh_video = msg->copy(); |
| 302 | + return ret; | ||
| 285 | } | 303 | } |
| 304 | + | ||
| 305 | + // cache the last gop packets | ||
| 306 | + if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { | ||
| 307 | + srs_error("shrink gop cache failed. ret=%d", ret); | ||
| 308 | + return ret; | ||
| 309 | + } | ||
| 310 | + srs_verbose("cache gop success."); | ||
| 286 | 311 | ||
| 287 | return ret; | 312 | return ret; |
| 288 | } | 313 | } |
| @@ -312,6 +337,16 @@ int SrsSource::on_video(SrsCommonMessage* video) | @@ -312,6 +337,16 @@ int SrsSource::on_video(SrsCommonMessage* video) | ||
| 312 | } | 337 | } |
| 313 | srs_info("dispatch audio sequence header success"); | 338 | srs_info("dispatch audio sequence header success"); |
| 314 | 339 | ||
| 340 | + std::vector<SrsSharedPtrMessage*>::iterator it; | ||
| 341 | + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { | ||
| 342 | + SrsSharedPtrMessage* msg = *it; | ||
| 343 | + if ((ret = consumer->enqueue(msg->copy())) != ERROR_SUCCESS) { | ||
| 344 | + srs_error("dispatch cached gop failed. ret=%d", ret); | ||
| 345 | + return ret; | ||
| 346 | + } | ||
| 347 | + } | ||
| 348 | + srs_trace("dispatch cached gop success. count=%d, duration=%d", (int)gop_cache.size(), consumer->get_time()); | ||
| 349 | + | ||
| 315 | return ret; | 350 | return ret; |
| 316 | } | 351 | } |
| 317 | 352 | ||
| @@ -325,3 +360,58 @@ void SrsSource::on_consumer_destroy(SrsConsumer* consumer) | @@ -325,3 +360,58 @@ void SrsSource::on_consumer_destroy(SrsConsumer* consumer) | ||
| 325 | srs_info("handle consumer destroy success."); | 360 | srs_info("handle consumer destroy success."); |
| 326 | } | 361 | } |
| 327 | 362 | ||
| 363 | +void SrsSource::on_unpublish() | ||
| 364 | +{ | ||
| 365 | + clear_gop_cache(); | ||
| 366 | + srs_trace("clear cache when unpublish."); | ||
| 367 | +} | ||
| 368 | + | ||
| 369 | +int SrsSource::cache_last_gop(SrsSharedPtrMessage* msg) | ||
| 370 | +{ | ||
| 371 | + int ret = ERROR_SUCCESS; | ||
| 372 | + | ||
| 373 | + if (!enable_gop_cache) { | ||
| 374 | + srs_verbose("gop cache is disabled."); | ||
| 375 | + return ret; | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + // got video, update the video count if acceptable | ||
| 379 | + if (msg->header.is_video()) { | ||
| 380 | + cached_video_count++; | ||
| 381 | + } | ||
| 382 | + | ||
| 383 | + // no acceptable video or pure audio, disable the cache. | ||
| 384 | + if (cached_video_count == 0) { | ||
| 385 | + srs_verbose("ignore any frame util got a h264 video frame."); | ||
| 386 | + return ret; | ||
| 387 | + } | ||
| 388 | + | ||
| 389 | + // clear gop cache when got key frame | ||
| 390 | + if (msg->header.is_video() && codec->video_is_keyframe(msg->payload, msg->size)) { | ||
| 391 | + srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", | ||
| 392 | + cached_video_count, (int)gop_cache.size()); | ||
| 393 | + | ||
| 394 | + clear_gop_cache(); | ||
| 395 | + | ||
| 396 | + // curent msg is video frame, so we set to 1. | ||
| 397 | + cached_video_count = 1; | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + // cache the frame. | ||
| 401 | + gop_cache.push_back(msg->copy()); | ||
| 402 | + | ||
| 403 | + return ret; | ||
| 404 | +} | ||
| 405 | + | ||
| 406 | +void SrsSource::clear_gop_cache() | ||
| 407 | +{ | ||
| 408 | + std::vector<SrsSharedPtrMessage*>::iterator it; | ||
| 409 | + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { | ||
| 410 | + SrsSharedPtrMessage* msg = *it; | ||
| 411 | + srs_freep(msg); | ||
| 412 | + } | ||
| 413 | + gop_cache.clear(); | ||
| 414 | + | ||
| 415 | + cached_video_count = 0; | ||
| 416 | +} | ||
| 417 | + |
| @@ -55,6 +55,10 @@ public: | @@ -55,6 +55,10 @@ public: | ||
| 55 | virtual ~SrsConsumer(); | 55 | virtual ~SrsConsumer(); |
| 56 | public: | 56 | public: |
| 57 | /** | 57 | /** |
| 58 | + * get current client time, the last packet time. | ||
| 59 | + */ | ||
| 60 | + virtual int get_time(); | ||
| 61 | + /** | ||
| 58 | * enqueue an shared ptr message. | 62 | * enqueue an shared ptr message. |
| 59 | */ | 63 | */ |
| 60 | virtual int enqueue(SrsSharedPtrMessage* msg); | 64 | virtual int enqueue(SrsSharedPtrMessage* msg); |
| @@ -92,6 +96,22 @@ private: | @@ -92,6 +96,22 @@ private: | ||
| 92 | SrsCodec* codec; | 96 | SrsCodec* codec; |
| 93 | std::string stream_url; | 97 | std::string stream_url; |
| 94 | std::vector<SrsConsumer*> consumers; | 98 | std::vector<SrsConsumer*> consumers; |
| 99 | +// gop cache for client fast startup. | ||
| 100 | +private: | ||
| 101 | + /** | ||
| 102 | + * if disabled the gop cache, | ||
| 103 | + * the client will wait for the next keyframe for h264, | ||
| 104 | + * and will be black-screen. | ||
| 105 | + */ | ||
| 106 | + bool enable_gop_cache; | ||
| 107 | + /** | ||
| 108 | + * the video frame count, avoid cache for pure audio stream. | ||
| 109 | + */ | ||
| 110 | + int cached_video_count; | ||
| 111 | + /** | ||
| 112 | + * cached gop. | ||
| 113 | + */ | ||
| 114 | + std::vector<SrsSharedPtrMessage*> gop_cache; | ||
| 95 | private: | 115 | private: |
| 96 | SrsSharedPtrMessage* cache_metadata; | 116 | SrsSharedPtrMessage* cache_metadata; |
| 97 | // the cached video sequence header. | 117 | // the cached video sequence header. |
| @@ -108,6 +128,15 @@ public: | @@ -108,6 +128,15 @@ public: | ||
| 108 | public: | 128 | public: |
| 109 | virtual int create_consumer(SrsConsumer*& consumer); | 129 | virtual int create_consumer(SrsConsumer*& consumer); |
| 110 | virtual void on_consumer_destroy(SrsConsumer* consumer); | 130 | virtual void on_consumer_destroy(SrsConsumer* consumer); |
| 131 | + virtual void on_unpublish(); | ||
| 132 | +private: | ||
| 133 | + /** | ||
| 134 | + * only for h264 codec | ||
| 135 | + * 1. cache the gop when got h264 video packet. | ||
| 136 | + * 2. clear gop when got keyframe. | ||
| 137 | + */ | ||
| 138 | + virtual int cache_last_gop(SrsSharedPtrMessage* msg); | ||
| 139 | + virtual void clear_gop_cache(); | ||
| 111 | }; | 140 | }; |
| 112 | 141 | ||
| 113 | #endif | 142 | #endif |
-
请 注册 或 登录 后发表评论