Skip to content

Commit f7bcb9d

Browse files
committed
Allow to optionally mmap() a stream in the image viewer.
1 parent 631a5db commit f7bcb9d

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

include/content-streamer.h

+33-8
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@
2222
namespace rgb_matrix {
2323
class FrameCanvas;
2424

25-
// An abstraction of a data stream.
25+
// An abstraction of a data stream. Two implementations exist for files and
26+
// an in-memory representation, but this allows your own implementation, e.g.
27+
// reading from a socket.
2628
class StreamIO {
2729
public:
2830
virtual ~StreamIO() {}
2931

3032
// Rewind stream.
3133
virtual void Rewind() = 0;
3234

33-
// Read bytes into buffer. Similar to Posix behavior that allows short reads.
35+
// Read bytes into buffer at current position of stream.
36+
// Similar to Posix behavior that allows short reads.
3437
virtual ssize_t Read(void *buf, size_t count) = 0;
3538

3639
// Write bytes from buffer. Similar to Posix behavior that allows short
@@ -43,25 +46,47 @@ class FileStreamIO : public StreamIO {
4346
explicit FileStreamIO(int fd);
4447
~FileStreamIO();
4548

46-
virtual void Rewind();
47-
virtual ssize_t Read(void *buf, size_t count);
48-
virtual ssize_t Append(const void *buf, size_t count);
49+
void Rewind() final;
50+
ssize_t Read(void *buf, size_t count) final;
51+
ssize_t Append(const void *buf, size_t count) final;
4952

5053
private:
5154
const int fd_;
5255
};
5356

57+
// Storing a stream in memory. Owns the memory.
5458
class MemStreamIO : public StreamIO {
5559
public:
56-
virtual void Rewind();
57-
virtual ssize_t Read(void *buf, size_t count);
58-
virtual ssize_t Append(const void *buf, size_t count);
60+
void Rewind() final;
61+
ssize_t Read(void *buf, size_t count) final;
62+
ssize_t Append(const void *buf, size_t count) final;
5963

6064
private:
6165
std::string buffer_; // super simplistic.
6266
size_t pos_;
6367
};
6468

69+
// Just a view around the memory, possibly a memory mapped file.
70+
class MemMapViewInput : public StreamIO {
71+
public:
72+
MemMapViewInput(int fd);
73+
~MemMapViewInput();
74+
75+
// Since mmmap() might fail, this tells us if it was successful.
76+
bool IsInitialized() const { return buffer_ != nullptr; }
77+
78+
void Rewind() final;
79+
ssize_t Read(void *buf, size_t count) final;
80+
81+
// No append, this is purely read-only.
82+
ssize_t Append(const void *buf, size_t count) final { return -1; }
83+
84+
private:
85+
char *buffer_;
86+
char *end_;
87+
char *pos_;
88+
};
89+
6590
class StreamWriter {
6691
public:
6792
// Does not take ownership of StreamIO

lib/content-streamer.cc

+36
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
#include "content-streamer.h"
44
#include "led-matrix.h"
55

6+
#include <cstddef>
67
#include <fcntl.h>
78
#include <stdio.h>
89
#include <string.h>
910
#include <sys/stat.h>
1011
#include <sys/types.h>
1112
#include <unistd.h>
13+
#include <sys/mman.h>
1214

1315
#include <algorithm>
1416

@@ -75,6 +77,40 @@ ssize_t MemStreamIO::Append(const void *buf, size_t count) {
7577
return count;
7678
}
7779

80+
MemMapViewInput::MemMapViewInput(int fd) : buffer_(nullptr) {
81+
struct stat s;
82+
if (fstat(fd, &s) < 0) {
83+
close(fd);
84+
perror("Couldn't get size");
85+
return; // Can't return error state from constructor. Stay uninitialized.
86+
}
87+
88+
const size_t file_size = s.st_size;
89+
buffer_ = (char*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
90+
close(fd);
91+
if (buffer_ == MAP_FAILED) {
92+
perror("Can't mmmap()");
93+
return;
94+
}
95+
end_ = buffer_ + file_size;
96+
#ifdef POSIX_MADV_WILLNEED
97+
// Trigger read-ahead if possible.
98+
posix_madvise(buffer_, file_size, POSIX_MADV_WILLNEED);
99+
#endif
100+
}
101+
102+
void MemMapViewInput::Rewind() { pos_ = buffer_; }
103+
ssize_t MemMapViewInput::Read(void *buf, size_t count) {
104+
if (pos_ + count >= end_) return -1;
105+
memcpy(buf, pos_, count);
106+
pos_ += count;
107+
return count;
108+
}
109+
110+
MemMapViewInput::~MemMapViewInput() {
111+
if (buffer_) munmap(buffer_, end_ - buffer_);
112+
}
113+
78114
// Read exactly count bytes including retries. Returns success.
79115
static bool FullRead(StreamIO *io, void *buf, const size_t count) {
80116
int remaining = count;

utils/led-image-viewer.cc

+20-4
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ struct ImageParams {
6262

6363
struct FileInfo {
6464
ImageParams params; // Each file might have specific timing settings
65-
bool is_multi_frame;
66-
rgb_matrix::StreamIO *content_stream;
65+
bool is_multi_frame = false;
66+
rgb_matrix::StreamIO *content_stream = nullptr;
6767
};
6868

6969
volatile bool interrupt_received = false;
@@ -209,6 +209,7 @@ static int usage(const char *progname) {
209209
fprintf(stderr, "Options:\n"
210210
"\t-O<streamfile> : Output to stream-file instead of matrix (Don't need to be root).\n"
211211
"\t-C : Center images.\n"
212+
"\t-m : if this is a stream, mmap() it. This can work around IO latencies in SD-card and refilling kernel buffers. This will use physical memory so only use if you have enough to map file size\n"
212213

213214
"\nThese options affect images FOLLOWING them on the command line,\n"
214215
"so it is possible to have different options for each image\n"
@@ -261,6 +262,7 @@ int main(int argc, char *argv[]) {
261262
return usage(argv[0]);
262263
}
263264

265+
bool do_mmap = false;
264266
bool do_forever = false;
265267
bool do_center = false;
266268
bool do_shuffle = false;
@@ -283,7 +285,7 @@ int main(int argc, char *argv[]) {
283285
const char *stream_output = NULL;
284286

285287
int opt;
286-
while ((opt = getopt(argc, argv, "w:t:l:fr:c:P:LhCR:sO:V:D:")) != -1) {
288+
while ((opt = getopt(argc, argv, "w:t:l:fr:c:P:LhCR:sO:V:D:m")) != -1) {
287289
switch (opt) {
288290
case 'w':
289291
img_param.wait_ms = roundf(atof(optarg) * 1000.0f);
@@ -297,6 +299,9 @@ int main(int argc, char *argv[]) {
297299
case 'D':
298300
img_param.anim_delay_ms = atoi(optarg);
299301
break;
302+
case 'm':
303+
do_mmap = true;
304+
break;
300305
case 'f':
301306
do_forever = true;
302307
break;
@@ -417,7 +422,18 @@ int main(int argc, char *argv[]) {
417422
if (fd >= 0) {
418423
file_info = new FileInfo();
419424
file_info->params = filename_params[filename];
420-
file_info->content_stream = new rgb_matrix::FileStreamIO(fd);
425+
if (do_mmap) {
426+
rgb_matrix::MemMapViewInput *stream_input =
427+
new rgb_matrix::MemMapViewInput(fd);
428+
if (stream_input->IsInitialized()) {
429+
file_info->content_stream = stream_input;
430+
} else {
431+
delete stream_input;
432+
}
433+
}
434+
if (!file_info->content_stream) {
435+
file_info->content_stream = new rgb_matrix::FileStreamIO(fd);
436+
}
421437
StreamReader reader(file_info->content_stream);
422438
if (reader.GetNext(offscreen_canvas, NULL)) { // header+size ok
423439
file_info->is_multi_frame = reader.GetNext(offscreen_canvas, NULL);

0 commit comments

Comments
 (0)