概述
最近我們項目有一個需求就是解決客戶端播放RTSP視頻流花屏的問題,一般來說丟包就會引起花屏,導致客戶端花屏的因素又有很多,比如說:
- 相機到服務器丟包
- 服務器到客戶端丟包
- 等等。。。
其中服務器到客戶端的丟包問題我們已經解決了,那么相機到服務器的丟包問題怎么解決呢?這個問題解決不了的,可以解決的問題就是即使相機到服務器丟包后,也讓客戶端知道,然后不解碼丟包的那一幀數據直到下一個關鍵幀的到來,這樣客戶端播放視頻就不會
花屏了,但是這樣做就會讓視頻播放卡頓一下(以50幀一個關鍵幀來算的話會卡頓2秒),但是卡頓總比花屏強吧,因為花屏后我們的AI服務器就采集不到人臉了,但是FFMpeg並沒有對外提供接口標志該AVPacket不完整,內部也不會將AVPacket丟棄,這樣服務端調用av_read_frame讀取的AVPacket時並不知道該包是否完整,一小段調用例子如下:
while (1) {
AVPacket pkt;
// 不知道pkt是否完整
ret = av_read_frame(f->ctx, &pkt);
if (ret == AVERROR(EAGAIN)) {
av_usleep(10000);
continue;
}
if (ret < 0) {
av_thread_message_queue_set_err_recv(f->in_thread_queue, ret);
break;
}
}
FFMpeg不提供接口,那么就只有修改FFMpeg源碼,瀏覽FFMpeg源碼一天后,對外的接口只需要在AVPacket結構體里面增加一個判斷包完整性的標志變量,修改源碼后的接口調用如下:
while (1) {
AVPacket pkt;
ret = av_read_frame(f->ctx, &pkt);
if (pkt.nLostPackets) {
// Do something.
} else {
// Do something
}
}
下面將介紹修改FFMpeg源碼的細節。
修改FFMpeg源碼(ffmpeg2.8.6)
一、avformat.h里面增加int av_read_frame_aozhen(AVFormatContext *s, AVPacket *pkt)函數:
並且在對應實現文件utils.c里面對其實現:
二、avcodec.h里面的AVPacket結構體增加成員變量int nIsLostPackets:
並且在avpacket.c里面的av_init_packets函數里面對其初始化:
三、utils.c里面read_frame_internal函數增加臨時變量int nIsLostPackets = 0,read_frame_internal函數調用ff_read_packet的后一句增加nIsLostPackets = cur_pkt.nIsLostPackets:
並且在函數末尾將nIsLostPackets賦值給pkt->nIsLostPackets:
四、在rtpdec.c的rtp_parse_queued_packet函數里面增加丟包判斷的代碼: