1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

support cache last gop for video

This commit is contained in:
winlin 2013-10-27 11:25:42 +08:00
parent b9ef2801d9
commit 0ed8807727
6 changed files with 176 additions and 7 deletions

View file

@ -45,7 +45,8 @@ url: rtmp://127.0.0.1:1935/live/livestream
* nginx v1.5.0: 139524 lines <br/>
### 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).

View file

@ -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;

View file

@ -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

View file

@ -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);
};

View file

@ -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<SrsSharedPtrMessage*>::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<SrsSharedPtrMessage*>::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;
}

View file

@ -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<SrsConsumer*> 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<SrsSharedPtrMessage*> 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