Skip to content

Commit ecb5db3

Browse files
Prefer using unique_ptr<PixelFrame> over shared_ptr<PixelFrame> (#171)
Summary: Pull Request resolved: #171 shared_ptr<PixelFrame> can look convenient, but it's heavier and riskier, in particular when multhreading is involved, as potential shared ownership (in particular accidental shared ownership) can make using/modifying objects unsafe. We don't have a good reason to use shared_ptr<PixelFrame> and as we're seeing apparent race conditions, unique_ptr<PixelFrame> is a better approach. Note: this doesn't resolve the issue we're hunting down, but it closes one possible explanation. Differential Revision: D68521262 fbshipit-source-id: 692e6de7da2367de7786bd68fa739344e6a559d4
1 parent 6e614a6 commit ecb5db3

File tree

6 files changed

+61
-40
lines changed

6 files changed

+61
-40
lines changed

tools/vrsplayer/FileReader.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ bool FileReader::isAtEnd() const {
144144

145145
void FileReader::closeFile() {
146146
stop();
147+
setState(FileReaderState::NoMedia);
147148
if (fileConfig_) {
148149
saveConfiguration();
149150
fileConfig_.reset();

tools/vrsplayer/FramePlayer.cpp

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222

2323
#define DEFAULT_LOG_CHANNEL "FramePlayer"
2424
#include <logging/Log.h>
25+
#include <logging/Verify.h>
2526

2627
#include <vrs/DiskFile.h>
2728
#include <vrs/IndexRecord.h>
2829

30+
#include "FileReader.h"
31+
2932
namespace vrsp {
3033

3134
using namespace std;
@@ -44,6 +47,9 @@ bool FramePlayer::onDataLayoutRead(
4447
const CurrentRecord& record,
4548
size_t blockIndex,
4649
DataLayout& layout) {
50+
if (!XR_VERIFY(state_ != FileReaderState::NoMedia)) {
51+
return false;
52+
}
4753
ostringstream buffer;
4854
layout.printLayoutCompact(buffer);
4955
string text = buffer.str();
@@ -62,12 +68,15 @@ bool FramePlayer::onImageRead(
6268
const CurrentRecord& record,
6369
size_t /*blockIndex*/,
6470
const ContentBlock& contentBlock) {
71+
if (!XR_VERIFY(state_ != FileReaderState::NoMedia)) {
72+
return false;
73+
}
6574
if (!saveNextFramePath_.empty()) {
6675
return saveFrame(record, contentBlock);
6776
}
6877
widget_->setDataFps(dataFps_.newFrame()); // fps counter for images read from file
6978
const auto& spec = contentBlock.image();
70-
shared_ptr<PixelFrame> frame = getFrame(true);
79+
unique_ptr<PixelFrame> frame = getFrame(spec.getPixelFormat());
7180
bool frameValid = false;
7281
imageFormat_ = spec.getImageFormat();
7382
if (imageFormat_ == vrs::ImageFormat::VIDEO) {
@@ -132,7 +141,7 @@ bool FramePlayer::onImageRead(
132141
widget_->swapImage(frame);
133142
}
134143
}
135-
recycle(frame, !needsConvertedFrame_);
144+
recycle(frame);
136145
return true; // read next blocks, if any
137146
}
138147

@@ -141,44 +150,52 @@ void FramePlayer::setVisible(bool visible) {
141150
widget_->setVisible(visible_);
142151
}
143152

144-
void FramePlayer::convertFrame(shared_ptr<PixelFrame>& frame) {
153+
void FramePlayer::convertFrame(unique_ptr<PixelFrame>& frame) {
145154
if (blankMode_) {
146155
makeBlankFrame(frame);
147156
} else {
148-
shared_ptr<PixelFrame> convertedFrame = needsConvertedFrame_ ? getFrame(false) : nullptr;
149-
PixelFrame::normalizeFrame(frame, convertedFrame, false, normalizeOptions_);
150-
needsConvertedFrame_ = (frame != convertedFrame); // for next time!
157+
unique_ptr<PixelFrame> convertedFrame =
158+
needsConvertedFrame_ ? getFrame(frame->getPixelFormat()) : nullptr;
159+
needsConvertedFrame_ =
160+
frame->normalizeFrame(PixelFrame::make(convertedFrame), false, normalizeOptions_);
151161
if (needsConvertedFrame_) {
152-
recycle(frame, true);
162+
recycle(frame);
153163
frame = std::move(convertedFrame);
154164
}
155165
}
156166
}
157167

158-
void FramePlayer::makeBlankFrame(shared_ptr<PixelFrame>& frame) {
168+
void FramePlayer::makeBlankFrame(unique_ptr<PixelFrame>& frame) {
159169
frame->init(vrs::PixelFormat::GREY8, frame->getWidth(), frame->getHeight());
160170
frame->blankFrame();
161171
}
162172

163-
shared_ptr<PixelFrame> FramePlayer::getFrame(bool inputNotConvertedFrame) {
173+
unique_ptr<PixelFrame> FramePlayer::getFrame(vrs::PixelFormat format) {
164174
unique_lock<mutex> lock(frameMutex_);
165-
vector<shared_ptr<PixelFrame>>& frames = inputNotConvertedFrame ? inputFrames_ : convertedframes_;
166-
if (frames.empty()) {
175+
if (recycledFrames_.empty()) {
167176
return nullptr;
168177
}
169-
shared_ptr<PixelFrame> frame = std::move(frames.back());
170-
frames.pop_back();
178+
if (recycledFrames_.back()->getPixelFormat() == format) {
179+
unique_ptr<PixelFrame> frame = std::move(recycledFrames_.back());
180+
recycledFrames_.pop_back();
181+
return frame;
182+
}
183+
unique_ptr<PixelFrame> frame = std::move(recycledFrames_.front());
184+
recycledFrames_.pop_front();
171185
return frame;
172186
}
173187

174-
void FramePlayer::recycle(shared_ptr<PixelFrame>& frame, bool inputNotConvertedFrame) {
188+
void FramePlayer::recycle(unique_ptr<PixelFrame>& frame) {
175189
if (frame) {
176190
{
177191
unique_lock<mutex> lock(frameMutex_);
178-
vector<shared_ptr<PixelFrame>>& frames =
179-
inputNotConvertedFrame ? inputFrames_ : convertedframes_;
180-
if (frames.size() < 10) {
181-
frames.emplace_back(std::move(frame));
192+
if (recycledFrames_.size() < 10) {
193+
if (recycledFrames_.empty() ||
194+
recycledFrames_.back()->getPixelFormat() == frame->getPixelFormat()) {
195+
recycledFrames_.emplace_back(std::move(frame));
196+
} else {
197+
recycledFrames_.emplace_front(std::move(frame));
198+
}
182199
}
183200
}
184201
frame.reset();
@@ -267,7 +284,10 @@ void FramePlayer::imageJobsThreadActivity() {
267284
while (imageJobs_.getJob(job)) {
268285
; // just skip!
269286
}
270-
shared_ptr<PixelFrame>& frame = *job;
287+
if (!XR_VERIFY(state_ != FileReaderState::NoMedia)) {
288+
continue;
289+
}
290+
unique_ptr<PixelFrame>& frame = *job;
271291
bool frameValid = false;
272292
vrs::ImageFormat imageFormat = frame->getImageFormat();
273293
if (imageFormat == vrs::ImageFormat::RAW) {
@@ -283,7 +303,7 @@ void FramePlayer::imageJobsThreadActivity() {
283303
widget_->swapImage(frame);
284304
}
285305
if (imageFormat != vrs::ImageFormat::VIDEO) {
286-
recycle(frame, !frameValid || !needsConvertedFrame_);
306+
recycle(frame);
287307
}
288308
}
289309
}

tools/vrsplayer/FramePlayer.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ using ::vrs::StreamPlayer;
5151
using ::vrs::utils::PixelFrame;
5252
using ::vrs::utils::VideoRecordFormatStreamPlayer;
5353

54-
using ImageJob = shared_ptr<PixelFrame>;
54+
using ImageJob = unique_ptr<PixelFrame>;
5555

5656
class FramePlayer : public QObject, public VideoRecordFormatStreamPlayer {
5757
Q_OBJECT
@@ -91,8 +91,7 @@ class FramePlayer : public QObject, public VideoRecordFormatStreamPlayer {
9191
private:
9292
std::mutex videoDecodingMutex_;
9393
std::mutex frameMutex_;
94-
vector<shared_ptr<PixelFrame>> inputFrames_;
95-
vector<shared_ptr<PixelFrame>> convertedframes_;
94+
std::deque<unique_ptr<PixelFrame>> recycledFrames_;
9695
vrs::utils::NormalizeOptions normalizeOptions_;
9796
bool needsConvertedFrame_{false};
9897
vrs::ImageFormat imageFormat_{vrs::ImageFormat::UNDEFINED};
@@ -110,10 +109,10 @@ class FramePlayer : public QObject, public VideoRecordFormatStreamPlayer {
110109

111110
vrs::JobQueueWithThread<std::unique_ptr<ImageJob>> imageJobs_;
112111

113-
void convertFrame(shared_ptr<PixelFrame>& frame);
114-
static void makeBlankFrame(shared_ptr<PixelFrame>& frame);
115-
shared_ptr<PixelFrame> getFrame(bool inputNotConvertedFrame);
116-
void recycle(shared_ptr<PixelFrame>& frame, bool inputNotConvertedFrame);
112+
void convertFrame(unique_ptr<PixelFrame>& frame);
113+
static void makeBlankFrame(unique_ptr<PixelFrame>& frame);
114+
unique_ptr<PixelFrame> getFrame(vrs::PixelFormat format);
115+
void recycle(unique_ptr<PixelFrame>& frame);
117116
bool saveFrame(const CurrentRecord& record, const ContentBlock& contentBlock);
118117
};
119118

tools/vrsplayer/FrameWidget.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ FrameWidget::FrameWidget(QWidget* parent) {
7171
connect(this, &FrameWidget::customContextMenuRequested, this, &FrameWidget::ShowContextMenu);
7272
}
7373

74+
FrameWidget::~FrameWidget() = default;
75+
7476
void FrameWidget::paintEvent(QPaintEvent* evt) {
7577
QPainter painter(this);
7678
painter.setRenderHint(QPainter::Antialiasing);
@@ -176,7 +178,7 @@ int FrameWidget::heightForWidth(int w) const {
176178
return (w * size.height()) / size.width();
177179
}
178180

179-
void FrameWidget::swapImage(shared_ptr<PixelFrame>& image) {
181+
void FrameWidget::swapImage(unique_ptr<PixelFrame>& image) {
180182
unique_lock<mutex> lock;
181183
imageFps_.newFrame();
182184
bool resize = image && image->qsize() != imageSize_;
@@ -198,17 +200,15 @@ int FrameWidget::saveImage(const std::string& path) {
198200
}
199201

200202
void FrameWidget::updateMinMaxSize() {
201-
if (image_) {
202-
QSize size = getImageSize();
203-
setMinimumSize(size.scaled(100, 100, Qt::KeepAspectRatio));
204-
setBaseSize(size);
203+
QSize size = getImageSize();
204+
setMinimumSize(size.scaled(100, 100, Qt::KeepAspectRatio));
205+
setBaseSize(size);
205206
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
206-
QSize screenSize = QApplication::desktop()->screenGeometry(this).size().operator*=(0.95);
207+
QSize screenSize = QApplication::desktop()->screenGeometry(this).size().operator*=(0.95);
207208
#else
208-
QSize screenSize = screen()->geometry().size() * 0.95;
209+
QSize screenSize = screen()->geometry().size() * 0.95;
209210
#endif
210-
setMaximumSize(size.scaled(screenSize, Qt::KeepAspectRatio));
211-
}
211+
setMaximumSize(size.scaled(screenSize, Qt::KeepAspectRatio));
212212
}
213213

214214
void FrameWidget::ShowContextMenu(const QPoint& pos) {

tools/vrsplayer/FrameWidget.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ class PixelFrame;
3737
namespace vrsp {
3838

3939
using std::map;
40-
using std::shared_ptr;
4140
using std::string;
41+
using std::unique_ptr;
4242
using vrs::utils::PixelFrame;
4343

4444
// Frame Per Second estimator class
@@ -78,6 +78,7 @@ class FrameWidget : public QWidget {
7878
Q_OBJECT
7979
public:
8080
explicit FrameWidget(QWidget* parent = nullptr);
81+
~FrameWidget() override;
8182

8283
void paintEvent(QPaintEvent* evt) override;
8384
QSize sizeHint() const override;
@@ -116,7 +117,7 @@ class FrameWidget : public QWidget {
116117
return flipped_;
117118
}
118119

119-
void swapImage(shared_ptr<PixelFrame>& image);
120+
void swapImage(unique_ptr<PixelFrame>& image);
120121
int saveImage(const string& path);
121122

122123
void setTypeToShow(Record::Type type) {
@@ -154,7 +155,7 @@ class FrameWidget : public QWidget {
154155

155156
private:
156157
std::mutex mutex_;
157-
shared_ptr<PixelFrame> image_;
158+
unique_ptr<PixelFrame> image_;
158159
string deviceTypeTag_;
159160
string deviceTypeConfig_;
160161
QSize imageSize_{640, 480};

vrs/utils/PixelFrame.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ PixelFrame::getStreamNormalizeOptions(RecordFileReader& reader, StreamId id, Pix
10351035
#if IS_VRS_OSS_CODE()
10361036

10371037
bool PixelFrame::normalizeToPixelFormat(
1038-
shared_ptr<PixelFrame>& convertedFrame,
1038+
PixelFrame& convertedFrame,
10391039
PixelFormat targetPixelFormat,
10401040
const NormalizeOptions& options) const {
10411041
return false;

0 commit comments

Comments
 (0)