]> git.sesse.net Git - nageru/commitdiff
Cache file descriptors when reading frames, for better readahead and fewer syscalls.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 24 Nov 2018 21:48:44 +0000 (22:48 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 25 Nov 2018 22:36:26 +0000 (23:36 +0100)
frame_on_disk.cpp [new file with mode: 0644]
frame_on_disk.h
jpeg_frame_view.cpp
jpeg_frame_view.h
main.cpp
meson.build
video_stream.cpp
video_stream.h

diff --git a/frame_on_disk.cpp b/frame_on_disk.cpp
new file mode 100644 (file)
index 0000000..b496b3d
--- /dev/null
@@ -0,0 +1,53 @@
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "frame_on_disk.h"
+
+using namespace std;
+
+FrameReader::~FrameReader()
+{
+       if (fd != -1) {
+               close(fd);
+       }
+}
+
+string FrameReader::read_frame(FrameOnDisk frame)
+{
+       if (int(frame.filename_idx) != last_filename_idx) {
+               if (fd != -1) {
+                       close(fd);  // Ignore errors.
+               }
+
+               string filename;
+               {
+                       lock_guard<mutex> lock(frame_mu);
+                       filename = frame_filenames[frame.filename_idx];
+               }
+
+               fd = open(filename.c_str(), O_RDONLY);
+               if (fd == -1) {
+                       perror(filename.c_str());
+                       exit(1);
+               }
+
+               // We want readahead. (Ignore errors.)
+               posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+
+               last_filename_idx = frame.filename_idx;
+       }
+
+       string str;
+       str.resize(frame.size);
+       off_t offset = 0;
+       while (offset < frame.size) {
+               int ret = pread(fd, &str[offset], frame.size - offset, frame.offset + offset);
+               if (ret <= 0) {
+                       perror("pread");
+                       exit(1);
+               }
+
+               offset += ret;
+       }
+       return str;
+}
index a0f46ad171ab6bf0e4d91a846a99727fae0c6691..184385792528130df7e3ba16637385c0c14a35db 100644 (file)
@@ -19,6 +19,19 @@ struct FrameOnDisk {
 extern std::vector<FrameOnDisk> frames[MAX_STREAMS];  // Under frame_mu.
 extern std::vector<std::string> frame_filenames;  // Under frame_mu.
 
-std::string read_frame(FrameOnDisk frame);
+// A helper class to read frames from disk. It caches the file descriptor
+// so that the kernel has a better chance of doing readahead when it sees
+// the sequential reads. (For this reason, each display has a private
+// FrameReader. Thus, we can easily keep multiple open file descriptors around
+// for a single .frames file.)
+class FrameReader {
+public:
+       ~FrameReader();
+       std::string read_frame(FrameOnDisk frame);
+
+private:
+       int fd = -1;
+       int last_filename_idx = -1;
+};
 
 #endif  // !defined(_FRAME_ON_DISK_H)
index be53688a03ee33bca3bb50688030978eb411ade2..3d3383fe887ea17c6fcab08abcd13752181a0bda 100644 (file)
@@ -207,7 +207,7 @@ void prune_cache()
        }
 }
 
-shared_ptr<Frame> decode_jpeg_with_cache(FrameOnDisk frame_spec, CacheMissBehavior cache_miss_behavior, bool *did_decode)
+shared_ptr<Frame> decode_jpeg_with_cache(FrameOnDisk frame_spec, CacheMissBehavior cache_miss_behavior, FrameReader *frame_reader, bool *did_decode)
 {
        *did_decode = false;
        {
@@ -224,7 +224,7 @@ shared_ptr<Frame> decode_jpeg_with_cache(FrameOnDisk frame_spec, CacheMissBehavi
        }
 
        *did_decode = true;
-       shared_ptr<Frame> frame = decode_jpeg(read_frame(frame_spec));
+       shared_ptr<Frame> frame = decode_jpeg(frame_reader->read_frame(frame_spec));
 
        unique_lock<mutex> lock(cache_mu);
        cache_bytes_used += frame_size(*frame);
@@ -236,7 +236,7 @@ shared_ptr<Frame> decode_jpeg_with_cache(FrameOnDisk frame_spec, CacheMissBehavi
        return frame;
 }
 
-void jpeg_decoder_thread_func()
+void JPEGFrameView::jpeg_decoder_thread_func()
 {
        size_t num_decoded = 0, num_dropped = 0;
 
@@ -281,7 +281,7 @@ void jpeg_decoder_thread_func()
                        }
 
                        bool found_in_cache;
-                       shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, cache_miss_behavior, &found_in_cache);
+                       shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, cache_miss_behavior, &decode.destination->frame_reader, &found_in_cache);
 
                        if (frame == nullptr) {
                                assert(cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE);
@@ -297,9 +297,9 @@ void jpeg_decoder_thread_func()
                                }
                        }
                        if (subframe_idx == 0) {
-                               primary_frame = move(frame);
+                               primary_frame = std::move(frame);
                        } else {
-                               secondary_frame = move(frame);
+                               secondary_frame = std::move(frame);
                        }
                }
                if (drop) {
index bc36283774e987c69dd14692210722ac4d10bca8..38ffd412b61b5610cc68e8fa6e646e6901f68571 100644 (file)
@@ -21,7 +21,7 @@ enum CacheMissBehavior {
 };
 
 std::shared_ptr<Frame> decode_jpeg(const std::string &filename);
-std::shared_ptr<Frame> decode_jpeg_with_cache(FrameOnDisk id, CacheMissBehavior cache_miss_behavior, bool *did_decode);
+std::shared_ptr<Frame> decode_jpeg_with_cache(FrameOnDisk id, CacheMissBehavior cache_miss_behavior, FrameReader *frame_reader, bool *did_decode);
 
 class JPEGFrameView : public QGLWidget {
        Q_OBJECT
@@ -50,6 +50,10 @@ protected:
        void paintGL() override;
 
 private:
+       static void jpeg_decoder_thread_func();
+
+       FrameReader frame_reader;
+
        // The stream index of the latest frame we displayed.
        unsigned current_stream_idx = 0;
 
index e04af2251240379c4288d4e72f7ab2ea670d42a0..681724a4ec4540d26b771bbf8ae4ee1be0f82b1f 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -421,33 +421,3 @@ int record_thread_func()
 
        return 0;
 }
-
-string read_frame(FrameOnDisk frame)
-{
-       string filename;
-       {
-               lock_guard<mutex> lock(frame_mu);
-               filename = frame_filenames[frame.filename_idx];
-       }
-
-       // TODO: cache the open file handles
-       FILE *fp = fopen(filename.c_str(), "rb");
-       if (fp == nullptr) {
-               perror(filename.c_str());
-               exit(1);
-       }
-       if (fseek(fp, frame.offset, SEEK_SET) == -1) {
-               perror("fseek");
-               exit(1);
-       }
-
-       string str;
-       str.resize(frame.size);
-       if (fread(&str[0], frame.size, 1, fp) != 1) {
-               perror("fread");
-               exit(1);
-       }
-
-       fclose(fp);
-       return str;
-}
index 874cb787ad6c36b256c13ca13fc719cf879ad12c..410d71308d62daaf3fb371eae8727274d0c11dc7 100644 (file)
@@ -44,7 +44,7 @@ srcs = ['flow.cpp', 'gpu_timers.cpp']
 # All the other files.
 srcs += ['ffmpeg_raii.cpp', 'main.cpp', 'player.cpp', 'httpd.cpp', 'mux.cpp', 'metacube2.cpp', 'video_stream.cpp', 'context.cpp', 'chroma_subsampler.cpp']
 srcs += ['vaapi_jpeg_decoder.cpp', 'memcpy_interleaved.cpp', 'db.cpp', 'disk_space_estimator.cpp', 'ycbcr_converter.cpp', 'flags.cpp']
-srcs += ['mainwindow.cpp', 'jpeg_frame_view.cpp', 'clip_list.cpp']
+srcs += ['mainwindow.cpp', 'jpeg_frame_view.cpp', 'clip_list.cpp', 'frame_on_disk.cpp']
 srcs += moc_files
 srcs += proto_generated
 
index a9ed0610e4518729e9aaa9614bb98ab0083d33ce..d425ed190b6fa3187a6f3311c3c980d7ff48c031 100644 (file)
@@ -294,7 +294,7 @@ void VideoStream::schedule_original_frame(steady_clock::time_point local_pts,
 
        // Preload the file from disk, so that the encoder thread does not get stalled.
        // TODO: Consider sending it through the queue instead.
-       (void)read_frame(frame);
+       (void)frame_reader.read_frame(frame);
 
        QueuedFrame qf;
        qf.local_pts = local_pts;
@@ -334,8 +334,8 @@ void VideoStream::schedule_faded_frame(steady_clock::time_point local_pts, int64
 
        bool did_decode;
 
-       shared_ptr<Frame> frame1 = decode_jpeg_with_cache(frame1_spec, DECODE_IF_NOT_IN_CACHE, &did_decode);
-       shared_ptr<Frame> frame2 = decode_jpeg_with_cache(frame2_spec, DECODE_IF_NOT_IN_CACHE, &did_decode);
+       shared_ptr<Frame> frame1 = decode_jpeg_with_cache(frame1_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
+       shared_ptr<Frame> frame2 = decode_jpeg_with_cache(frame2_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
 
        ycbcr_semiplanar_converter->prepare_chain_for_fade(frame1, frame2, fade_alpha)->render_to_fbo(resources->fade_fbo, 1280, 720);
 
@@ -414,7 +414,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts
        for (size_t frame_no = 0; frame_no < 2; ++frame_no) {
                FrameOnDisk frame_spec = frame_no == 1 ? frame2 : frame1;
                bool did_decode;
-               shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, DECODE_IF_NOT_IN_CACHE, &did_decode);
+               shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
                ycbcr_converter->prepare_chain_for_conversion(frame)->render_to_fbo(resources->input_fbos[frame_no], 1280, 720);
        }
 
@@ -434,7 +434,7 @@ void VideoStream::schedule_interpolated_frame(steady_clock::time_point local_pts
 
                // Now decode the image we are fading against.
                bool did_decode;
-               shared_ptr<Frame> frame2 = decode_jpeg_with_cache(secondary_frame, DECODE_IF_NOT_IN_CACHE, &did_decode);
+               shared_ptr<Frame> frame2 = decode_jpeg_with_cache(secondary_frame, DECODE_IF_NOT_IN_CACHE, &frame_reader, &did_decode);
 
                // Then fade against it, putting it into the fade Y' and CbCr textures.
                ycbcr_semiplanar_converter->prepare_chain_for_fade_from_texture(qf.output_tex, frame2, fade_alpha)->render_to_fbo(resources->fade_fbo, 1280, 720);
@@ -566,7 +566,7 @@ void VideoStream::encode_thread_func()
 
                if (qf.type == QueuedFrame::ORIGINAL) {
                        // Send the JPEG frame on, unchanged.
-                       string jpeg = read_frame(qf.frame1);
+                       string jpeg = frame_reader.read_frame(qf.frame1);
                        AVPacket pkt;
                        av_init_packet(&pkt);
                        pkt.stream_index = 0;
index 6cc9471befde29a25aecdc163d0956ebc21f6a37..736a20fe275ca7acacf9bd6be1e79377877840ac 100644 (file)
@@ -62,6 +62,8 @@ public:
                                    QueueSpotHolder &&queue_spot_holder);
 
 private:
+       FrameReader frame_reader;
+
        void encode_thread_func();
        std::thread encode_thread;