Skip to content

Commit af8c59a

Browse files
committed
Refactor out the logic for holding the last frame.
1 parent cef7ce1 commit af8c59a

File tree

6 files changed

+90
-62
lines changed

6 files changed

+90
-62
lines changed

SerialPrograms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ file(GLOB MAIN_SOURCES
463463
Source/CommonFramework/VideoPipeline/Backends/CameraWidgetQt6.h
464464
Source/CommonFramework/VideoPipeline/Backends/MediaServicesQt6.cpp
465465
Source/CommonFramework/VideoPipeline/Backends/MediaServicesQt6.h
466+
Source/CommonFramework/VideoPipeline/Backends/QVideoFrameCache.h
466467
Source/CommonFramework/VideoPipeline/Backends/VideoFrameQt.h
467468
Source/CommonFramework/VideoPipeline/CameraInfo.h
468469
Source/CommonFramework/VideoPipeline/Stats/CpuUtilizationStats.h

SerialPrograms/Source/CommonFramework/VideoPipeline/Backends/CameraWidgetQt6.5.cpp

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ CameraVideoSource::CameraVideoSource(
9595
, m_logger(logger)
9696
, m_last_image_timestamp(WallClock::min())
9797
, m_stats_conversion("ConvertFrame", "ms", 1000, std::chrono::seconds(10))
98-
, m_last_frame_seqnum(0)
9998
{
10099
if (!info){
101100
return;
@@ -182,19 +181,8 @@ void CameraVideoSource::set_video_output(QGraphicsVideoItem& item){
182181
// return immediately to unblock the main thread.
183182

184183
WallClock now = current_time();
185-
{
186-
WriteSpinLock lg(m_frame_lock);
187-
188-
// Skip duplicate frames.
189-
if (frame.startTime() != -1 && frame.startTime() <= m_last_frame.startTime()){
190-
return;
191-
}
192-
193-
m_last_frame = frame;
194-
m_last_frame_timestamp = now;
195-
uint64_t seqnum = m_last_frame_seqnum.load(std::memory_order_relaxed);
196-
seqnum++;
197-
m_last_frame_seqnum.store(seqnum, std::memory_order_relaxed);
184+
if (!m_last_frame.push_frame(frame, now)){
185+
return;
198186
}
199187
report_source_frame(std::make_shared<VideoFrame>(now, frame));
200188
},
@@ -214,20 +202,15 @@ VideoSnapshot CameraVideoSource::snapshot(){
214202
std::lock_guard<std::mutex> lg(m_cache_lock);
215203

216204
// Check the cached image frame. If it's not stale, return it immediately.
217-
uint64_t frame_seqnum = m_last_frame_seqnum.load(std::memory_order_relaxed);
205+
uint64_t frame_seqnum = m_last_frame.seqnum();
218206
if (!m_last_image.isNull() && m_last_image_seqnum == frame_seqnum){
219207
return VideoSnapshot(m_last_image, m_last_image_timestamp);
220208
}
221209

222210
// Cached image is stale. Grab the latest frame.
223211
QVideoFrame frame;
224212
WallClock frame_timestamp;
225-
{
226-
ReadSpinLock lg0(m_frame_lock);
227-
frame_seqnum = m_last_frame_seqnum.load(std::memory_order_relaxed);
228-
frame = m_last_frame;
229-
frame_timestamp = m_last_frame_timestamp;
230-
}
213+
frame_seqnum = m_last_frame.get_latest(frame, frame_timestamp);
231214

232215
if (!frame.isValid()){
233216
m_logger.log("QVideoFrame is null.", COLOR_RED);

SerialPrograms/Source/CommonFramework/VideoPipeline/Backends/CameraWidgetQt6.5.h

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include <QtGlobal>
1111
#if QT_VERSION_MAJOR == 6
1212

13-
#include <set>
13+
//#include <set>
1414
#include <mutex>
1515
#include <QCameraDevice>
1616
#include <QMediaCaptureSession>
@@ -24,8 +24,13 @@
2424
#include "CommonFramework/Tools/StatAccumulator.h"
2525
#include "CommonFramework/VideoPipeline/VideoSource.h"
2626
#include "CommonFramework/VideoPipeline/CameraInfo.h"
27+
#include "QVideoFrameCache.h"
2728
#include "CameraImplementations.h"
2829

30+
// REMOVE
31+
#include "Common/Cpp/Concurrency/AsyncDispatcher.h"
32+
//#include "Common/Cpp/Containers/CircularBuffer.h"
33+
2934
//#include <iostream>
3035
//using std::cout;
3136
//using std::endl;
@@ -87,6 +92,8 @@ class StaticQGraphicsView : public QGraphicsView{
8792
};
8893

8994

95+
96+
9097
class CameraVideoSource : public QObject, public VideoSource{
9198
public:
9299
virtual ~CameraVideoSource();
@@ -135,16 +142,7 @@ class CameraVideoSource : public QObject, public VideoSource{
135142
PeriodicStatsReporterI32 m_stats_conversion;
136143

137144
private:
138-
// Last Frame: All accesses must be under this lock.
139-
// These will be updated very rapidly by the main thread.
140-
// Holding the frame lock will block the main thread.
141-
// So accessors should minimize the time they hold the frame lock.
142-
143-
mutable SpinLock m_frame_lock;
144-
145-
QVideoFrame m_last_frame;
146-
WallClock m_last_frame_timestamp;
147-
std::atomic<uint64_t> m_last_frame_seqnum;
145+
QVideoFrameCache m_last_frame;
148146

149147
};
150148

SerialPrograms/Source/CommonFramework/VideoPipeline/Backends/CameraWidgetQt6.cpp

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ CameraVideoSource::CameraVideoSource(
8888
, m_logger(logger)
8989
, m_last_image_timestamp(WallClock::min())
9090
, m_stats_conversion("ConvertFrame", "ms", 1000, std::chrono::seconds(10))
91-
, m_last_frame_seqnum(0)
9291
{
9392
if (!info){
9493
return;
@@ -160,14 +159,8 @@ CameraVideoSource::CameraVideoSource(
160159
// return immediately to unblock the main thread.
161160

162161
WallClock now = current_time();
163-
{
164-
WriteSpinLock lg(m_frame_lock);
165-
// cout << now << endl;
166-
m_last_frame = frame;
167-
m_last_frame_timestamp = now;
168-
uint64_t seqnum = m_last_frame_seqnum.load(std::memory_order_relaxed);
169-
seqnum++;
170-
m_last_frame_seqnum.store(seqnum, std::memory_order_relaxed);
162+
if (!m_last_frame.push_frame(frame, now)){
163+
return;
171164
}
172165
report_source_frame(std::make_shared<VideoFrame>(now, frame));
173166
}
@@ -184,19 +177,16 @@ VideoSnapshot CameraVideoSource::snapshot(){
184177
return VideoSnapshot();
185178
}
186179

187-
// Frame is already cached and is not stale.
180+
// Check the cached image frame. If it's not stale, return it immediately.
181+
uint64_t frame_seqnum = m_last_frame.seqnum();
182+
if (!m_last_image.isNull() && m_last_image_seqnum == frame_seqnum){
183+
return VideoSnapshot(m_last_image, m_last_image_timestamp);
184+
}
185+
186+
// Cached image is stale. Grab the latest frame.
188187
QVideoFrame frame;
189188
WallClock frame_timestamp;
190-
uint64_t frame_seqnum;
191-
{
192-
SpinLockGuard lg0(m_frame_lock);
193-
frame_seqnum = m_last_frame_seqnum;
194-
if (!m_last_image.isNull() && m_last_image_seqnum == frame_seqnum){
195-
return VideoSnapshot(m_last_image, m_last_image_timestamp);
196-
}
197-
frame = m_last_frame;
198-
frame_timestamp = m_last_frame_timestamp;
199-
}
189+
frame_seqnum = m_last_frame.get_latest(frame, frame_timestamp);
200190

201191
if (!frame.isValid()){
202192
global_logger_tagged().log("QVideoFrame is null.", COLOR_RED);

SerialPrograms/Source/CommonFramework/VideoPipeline/Backends/CameraWidgetQt6.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "CommonFramework/Tools/StatAccumulator.h"
2020
#include "CommonFramework/VideoPipeline/VideoSource.h"
2121
#include "CommonFramework/VideoPipeline/CameraInfo.h"
22+
#include "QVideoFrameCache.h"
2223
#include "CameraImplementations.h"
2324

2425
class QCamera;
@@ -96,16 +97,7 @@ class CameraVideoSource : public QObject, public VideoSource{
9697

9798

9899
private:
99-
// Last Frame: All accesses must be under this lock.
100-
// These will be updated very rapidly by the main thread.
101-
// Holding the frame lock will block the main thread.
102-
// So accessors should minimize the time they hold the frame lock.
103-
104-
mutable SpinLock m_frame_lock;
105-
106-
QVideoFrame m_last_frame;
107-
WallClock m_last_frame_timestamp;
108-
std::atomic<uint64_t> m_last_frame_seqnum;
100+
QVideoFrameCache m_last_frame;
109101

110102
};
111103

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* Camera Widget (Qt6.5)
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
*/
6+
7+
#ifndef PokemonAutomation_VideoPipeline_QVideoFrameCache_H
8+
#define PokemonAutomation_VideoPipeline_QVideoFrameCache_H
9+
10+
#include <QVideoFrame>
11+
#include "Common/Cpp/Time.h"
12+
#include "Common/Cpp/Concurrency/SpinLock.h"
13+
14+
namespace PokemonAutomation{
15+
16+
17+
18+
class QVideoFrameCache{
19+
public:
20+
QVideoFrameCache()
21+
: m_last_frame_timestamp(WallClock::min())
22+
, m_last_frame_seqnum(0)
23+
{}
24+
25+
uint64_t seqnum() const{
26+
return m_last_frame_seqnum.load(std::memory_order_relaxed);
27+
}
28+
uint64_t get_latest(QVideoFrame& frame, WallClock& timestamp) const{
29+
WriteSpinLock lg(m_frame_lock);
30+
frame = m_last_frame;
31+
timestamp = m_last_frame_timestamp;
32+
return seqnum();
33+
}
34+
35+
bool push_frame(QVideoFrame frame, WallClock timestamp){
36+
WriteSpinLock lg(m_frame_lock);
37+
38+
// Skip duplicate frames.
39+
if (frame.startTime() != -1 && frame.startTime() <= m_last_frame.startTime()){
40+
return false;
41+
}
42+
43+
m_last_frame = frame;
44+
m_last_frame_timestamp = timestamp;
45+
uint64_t seqnum = m_last_frame_seqnum.load(std::memory_order_relaxed);
46+
seqnum++;
47+
m_last_frame_seqnum.store(seqnum, std::memory_order_relaxed);
48+
49+
return true;
50+
}
51+
52+
53+
private:
54+
mutable SpinLock m_frame_lock;
55+
56+
QVideoFrame m_last_frame;
57+
WallClock m_last_frame_timestamp;
58+
std::atomic<uint64_t> m_last_frame_seqnum;
59+
};
60+
61+
62+
63+
}
64+
#endif

0 commit comments

Comments
 (0)