]> git.sesse.net Git - nageru/commitdiff
Make VideoStream capable of using the shared JPEG cache, saving lots of CPU.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 15 Sep 2018 20:19:12 +0000 (22:19 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 15 Sep 2018 20:19:12 +0000 (22:19 +0200)
jpeg_frame_view.cpp
jpeg_frame_view.h
video_stream.cpp

index 3d35e5e8818e11254458543c30038947efbc0d7b..5ef2f0b8ab3a66fb661ebb384eb2717420c5423c 100644 (file)
 using namespace movit;
 using namespace std;
 
-struct JPEGID {
-       unsigned stream_idx;
-       int64_t pts;
-};
 bool operator< (const JPEGID &a, const JPEGID &b) {
        return make_pair(a.stream_idx, a.pts) < make_pair(b.stream_idx, b.pts);
 }
@@ -146,6 +142,34 @@ void prune_cache()
        }
 }
 
+shared_ptr<Frame> decode_jpeg_with_cache(JPEGID id, CacheMissBehavior cache_miss_behavior, bool *did_decode)
+{
+       *did_decode = false;
+       {
+               unique_lock<mutex> lock(cache_mu);
+               auto it = cache.find(id);
+               if (it != cache.end()) {
+                       it->second.last_used = event_counter++;
+                       return it->second.frame;
+               }
+       }
+
+       if (cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE) {
+               return nullptr;
+       }
+
+       *did_decode = true;
+       shared_ptr<Frame> frame = decode_jpeg(filename_for_frame(id.stream_idx, id.pts));
+
+       unique_lock<mutex> lock(cache_mu);
+       cache[id] = LRUFrame{ frame, event_counter++ };
+
+       if (cache.size() > CACHE_SIZE) {
+               prune_cache();
+       }
+       return frame;
+}
+
 void jpeg_decoder_thread()
 {
        size_t num_decoded = 0, num_dropped = 0;
@@ -154,9 +178,9 @@ void jpeg_decoder_thread()
        for ( ;; ) {
                JPEGID id;
                JPEGFrameView *dest;
-               shared_ptr<Frame> frame;
+               CacheMissBehavior cache_miss_behavior = DECODE_IF_NOT_IN_CACHE;
                {
-                       unique_lock<mutex> lock(cache_mu);
+                       unique_lock<mutex> lock(cache_mu);  // TODO: Perhaps under another lock?
                        any_pending_decodes.wait(lock, [] {
                                return !pending_decodes.empty();
                        });
@@ -164,17 +188,6 @@ void jpeg_decoder_thread()
                        dest = pending_decodes.front().second;
                        pending_decodes.pop_front();
 
-                       auto it = cache.find(id);
-                       if (it != cache.end()) {
-                               frame = it->second.frame;
-                               it->second.last_used = event_counter++;
-                       }
-               }
-
-               if (frame == nullptr) {
-                       // Not found in the cache, so we need to do a decode or drop the request.
-                       // Prune the queue if there are too many pending for this destination.
-                       // TODO: Could we get starvation here?
                        size_t num_pending = 0;
                        for (const pair<JPEGID, JPEGFrameView *> &decode : pending_decodes) {
                                if (decode.second == dest) {
@@ -182,18 +195,20 @@ void jpeg_decoder_thread()
                                }
                        }
                        if (num_pending > 3) {
-                               ++num_dropped;
-                               continue;
+                               cache_miss_behavior = RETURN_NULLPTR_IF_NOT_IN_CACHE;
                        }
+               }
 
-                       frame = decode_jpeg(filename_for_frame(id.stream_idx, id.pts));
+               bool found_in_cache;
+               shared_ptr<Frame> frame = decode_jpeg_with_cache(id, cache_miss_behavior, &found_in_cache);
 
-                       unique_lock<mutex> lock(cache_mu);
-                       cache[id] = LRUFrame{ frame, event_counter++ };
+               if (frame == nullptr) {
+                       assert(cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE);
+                       ++num_dropped;
+                       continue;
+               }
 
-                       if (cache.size() > CACHE_SIZE) {
-                               prune_cache();
-                       }
+               if (!found_in_cache) {
                        ++num_decoded;
                        if (num_decoded % 1000 == 0) {
                                fprintf(stderr, "Decoded %zu images, dropped %zu (%.2f%% dropped)\n",
index 1f7b2b7a67108a88e6baaaddcdbd436d0b746b4e..5d97b074b5d706164ce33542053d86448a97fac4 100644 (file)
 
 #include <memory>
 
+struct JPEGID {
+       unsigned stream_idx;
+       int64_t pts;
+};
 struct Frame {
        std::unique_ptr<uint8_t[]> y, cb, cr;
        unsigned width, height;
        unsigned chroma_subsampling_x, chroma_subsampling_y;
        unsigned pitch_y, pitch_chroma;
 };
+enum CacheMissBehavior {
+       DECODE_IF_NOT_IN_CACHE,
+       RETURN_NULLPTR_IF_NOT_IN_CACHE
+};
 
 std::string filename_for_frame(unsigned stream_idx, int64_t pts);
 std::shared_ptr<Frame> decode_jpeg(const std::string &filename);
+std::shared_ptr<Frame> decode_jpeg_with_cache(JPEGID id, CacheMissBehavior cache_miss_behavior, bool *did_decode);
 
 class JPEGFrameView : public QGLWidget {
        Q_OBJECT
index 820a9335846c9115ed2e2c5ac2bba96819bd330d..625771fe410a2539cd53de0adad8feeb0eaeb50b 100644 (file)
@@ -295,7 +295,11 @@ void VideoStream::schedule_interpolated_frame(int64_t output_pts, unsigned strea
        // Convert frame0 and frame1 to OpenGL textures.
        // TODO: Deduplicate against JPEGFrameView::setDecodedFrame?
        for (size_t frame_no = 0; frame_no < 2; ++frame_no) {
-               shared_ptr<Frame> frame = decode_jpeg(filename_for_frame(stream_idx, frame_no == 1 ? input_second_pts : input_first_pts));
+               JPEGID jpeg_id;
+               jpeg_id.stream_idx = stream_idx;
+               jpeg_id.pts = frame_no == 1 ? input_second_pts : input_first_pts;
+               bool did_decode;
+               shared_ptr<Frame> frame = decode_jpeg_with_cache(jpeg_id, DECODE_IF_NOT_IN_CACHE, &did_decode);
                ycbcr_format.chroma_subsampling_x = frame->chroma_subsampling_x;
                ycbcr_format.chroma_subsampling_y = frame->chroma_subsampling_y;
                ycbcr_input->change_ycbcr_format(ycbcr_format);