因为在验证阶段,使用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关掉就能够解决这个问题了。