因爲在驗證階段,使用FPGA時咱們的算法檢測速度很慢,無法直接在主流上進行繪圖,不然的話,主流就要等待算法好久才能出圖。因此,咱們的解決方案是把框推到客戶端上,在客戶端上進行繪圖。html
這時,客戶端不只收到圖像幀,音頻幀,還會收到一個框信息,須要把三者進行同步顯示,不能圖像、音頻、框不匹配。而圖像、音頻都是經過ffmpeg寫入的,不會有問題,而檢測算法這邊是獨立於前面的出圖進程,沒有經過ffmpeg打包,因此須要使用ntp時間進行同步。c++
問題:web
AVPackate->pts
中兩幀之間的差是:50223us,也就是多了223us。ffmpeg的時間戳介紹:
http://www.javashuo.com/article/p-uivycgmu-bm.html算法
因爲ffmpeg把一切都封起來了,剛開始的時候發現只能獲得pts,而pts的結果是經過計算獲得的,它是相對第1幀的相對時間的,而框是絕對時間,沒法進行二者的匹配。websocket
打算在AVPacket
中添加一個first_rtcp_ntp_time
,這樣即可以經過與AVPacket
中的pts合併計算獲得真實的ntp時間。curl
分析代碼,發現pts是在libavofrmat/rtpdec.c
finalize_packet
函數中計算賦值的,同理,咱們也在這個函數計算first_rtcp_ntp_time
:socket
pkt->first_rtcp_ntp_time = av_rescale(s->first_rtcp_ntp_time,
s->st->time_base.den,
(uint64_t) s->st->time_base.num << 32);
寫個程序測試,打印該值,並無寫入到AVPacket中,發現,實際上ffmpeg中內部處理時會新建臨時packet對象,而在賦值的時候,以前的代碼並無進行這個字段的拷貝,因此致使值沒有傳出來,須要修改libavformat/utils.c
的parse_packet
函數:tcp
out_pkt.stream_index = st->index;
out_pkt.pts = st->parser->pts;
out_pkt.dts = st->parser->dts;
out_pkt.pos = st->parser->pos;
/* 新增以下行 */
out_pkt.first_rtcp_ntp_time = pkt->first_rtcp_ntp_time;
實際上,後來忽然發如今AVFormatContext中存在一個start_time_realtime
字段,裏面存儲了ntp時間,因此能夠直接利用這個值,不須要修改AVPacket
。但存在一個問題,這邊在計算時,對這個時間減去了一個NTP_OFFSET(1900~1970的時間差),而咱們在編碼時並無加上該偏移,這邊至關於多減了NTP_OFFSET,因此使用的時候須要把這個值再加回來。ide
s->start_time_realtime = av_rescale (rtpctx->first_rtcp_ntp_time - (NTP_OFFSET << 32), 1000000, 1LL << 32);
OpenCV中包含了一個cap.get(CV_CAP_PROP_POS_MSEC)函數用於獲取當前幀時間,其實現方式以下,主要是經過內部維護的frame_number與fps進行計算,但好像對咱們沒什麼用:函數
case CV_FFMPEG_CAP_PROP_POS_MSEC: return 1000.0*(double)frame_number/get_fps();
OpenCV調用ffmpeg進行rtsp解析的主要流程,最終仍是調用av_read_frame()函數:
在VideoCapture中提供一個成員函數cap.getRealTimestamp()用於返回時間戳,
CvCapture_FFMPEG中提供一個timestamp成員用來保存時間戳,提供一個接口函數,以下:
@@ -423,6 +423,7 @@ struct CvCapture_FFMPEG double get_duration_sec() const;
double get_fps() const;
int get_bitrate() const;
+ int64_t getRealTimestamp() const { return timestamp; }
AVRational get_sample_aspect_ratio(AVStream *stream) const;
@@ -436,6 +437,7 @@ struct CvCapture_FFMPEG AVFrame * picture;
AVFrame rgb_picture;
int64_t picture_pts;
+ int64_t timestamp;
最後在CvCapture_FFMPEG grabFrame函數中計算時間戳:
int64_t time = packet.first_rtcp_ntp_time + packet.pts;
AVRational in_base = {1, 90000};
AVRational out_base = {1, 1000000};
timestamp = av_rescale_q(time, in_base, out_base);
若是是使用start_time_realtime
則有:
#define NTP_OFFSET 2208988800ULL
int64_t time = av_rescale (ic->start_time_realtime, 1LL << 32, 1000000) + (NTP_OFFSET << 32);
timestamp = av_rescale (time, 1000000, 1LL << 32) + av_rescale (packet.pts, 1000000, 90000);
新建一個線程使用libcurl獲取websocket傳過來的框信息,放入到一個std::list容器中進行管理。
分別獲取幀時間與框時間進行比較,知足條件則繪圖:
list<struct box_result>::iterator it;
/* frame time */
int64_t frame_time = cap.getRealTimestamp();
/* compare and draw */
pthread_mutex_lock(&mutex);
for (it = l.begin(); it != l.end();) {
int64_t diff = it->timestamp - frame_time;
if (diff < DROP_TIME * 1.3) {
it = l.erase(it);
} else if (diff < -WAIT_TIME*0.1) {
for (int i = 0; i < it->box_num; ++i)
rectangle(frame, it->box[i].start, it->box[i].end, it->box[i].color, 2);
it++;
} else
break;
}
pthread_mutex_unlock(&mutex);
經過打印發現,ffmpeg中計算的地方結果是正確的,但opencv取到的結果確是錯誤的。
查看代碼,發現有進行wrap操做:
pkt->dts = wrap_timestamp(st, pkt->dts); pkt->pts = wrap_timestamp(st, pkt->pts);
經查文檔發現,能夠在打開流時,將correct_ts_overflow關掉就能夠解決這個問題了。