]> git.sesse.net Git - nageru/blobdiff - jpeg_frame_view.cpp
Allow symlinked frame files. Useful for testing.
[nageru] / jpeg_frame_view.cpp
index c610227e9cedb303b0a1e793151862e4ef5a7c7d..3d3383fe887ea17c6fcab08abcd13752181a0bda 100644 (file)
@@ -30,15 +30,18 @@ using namespace std;
 namespace {
 
 // Just an arbitrary order for std::map.
-struct JPEGIDLexicalOrder
+struct FrameOnDiskLexicalOrder
 {
-       bool operator() (const JPEGID &a, const JPEGID &b) const
+       bool operator() (const FrameOnDisk &a, const FrameOnDisk &b) const
        {
-               if (a.stream_idx != b.stream_idx)
-                       return a.stream_idx < b.stream_idx;
                if (a.pts != b.pts)
                        return a.pts < b.pts;
-               return a.interpolated < b.interpolated;
+               if (a.offset != b.offset)
+                       return a.offset < b.offset;
+               if (a.filename_idx != b.filename_idx)
+                       return a.filename_idx < b.filename_idx;
+               assert(a.size == b.size);
+               return false;
        }
 };
 
@@ -55,16 +58,23 @@ struct LRUFrame {
 };
 
 struct PendingDecode {
-       JPEGID primary, secondary;
-       float fade_alpha;  // Irrelevant if secondary.stream_idx == -1.
        JPEGFrameView *destination;
+
+       // For actual decodes (only if frame below is nullptr).
+       FrameOnDisk primary, secondary;
+       float fade_alpha;  // Irrelevant if secondary.stream_idx == -1.
+
+       // Already-decoded frames are also sent through PendingDecode,
+       // so that they get drawn in the right order. If frame is nullptr,
+       // it's a real decode.
+       shared_ptr<Frame> frame;
 };
 
 }  // namespace
 
 thread JPEGFrameView::jpeg_decoder_thread;
 mutex cache_mu;
-map<JPEGID, LRUFrame, JPEGIDLexicalOrder> cache;  // Under cache_mu.
+map<FrameOnDisk, LRUFrame, FrameOnDiskLexicalOrder> cache;  // Under cache_mu.
 size_t cache_bytes_used = 0;  // Under cache_mu.
 condition_variable any_pending_decodes;
 deque<PendingDecode> pending_decodes;  // Under cache_mu.
@@ -197,12 +207,12 @@ void prune_cache()
        }
 }
 
-shared_ptr<Frame> decode_jpeg_with_cache(JPEGID id, 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;
        {
                unique_lock<mutex> lock(cache_mu);
-               auto it = cache.find(id);
+               auto it = cache.find(frame_spec);
                if (it != cache.end()) {
                        it->second.last_used = event_counter++;
                        return it->second.frame;
@@ -213,13 +223,12 @@ shared_ptr<Frame> decode_jpeg_with_cache(JPEGID id, CacheMissBehavior cache_miss
                return nullptr;
        }
 
-       assert(!id.interpolated);
        *did_decode = true;
-       shared_ptr<Frame> frame = decode_jpeg(filename_for_frame(id.stream_idx, id.pts));
+       shared_ptr<Frame> frame = decode_jpeg(frame_reader->read_frame(frame_spec));
 
        unique_lock<mutex> lock(cache_mu);
        cache_bytes_used += frame_size(*frame);
-       cache[id] = LRUFrame{ frame, event_counter++ };
+       cache[frame_spec] = LRUFrame{ frame, event_counter++ };
 
        if (cache_bytes_used > size_t(CACHE_SIZE_MB) * 1024 * 1024) {
                prune_cache();
@@ -227,7 +236,7 @@ shared_ptr<Frame> decode_jpeg_with_cache(JPEGID id, CacheMissBehavior cache_miss
        return frame;
 }
 
-void jpeg_decoder_thread_func()
+void JPEGFrameView::jpeg_decoder_thread_func()
 {
        size_t num_decoded = 0, num_dropped = 0;
 
@@ -256,37 +265,26 @@ void jpeg_decoder_thread_func()
                        }
                }
 
+               if (decode.frame != nullptr) {
+                       // Already decoded, so just show it.
+                       decode.destination->setDecodedFrame(decode.frame, nullptr, 1.0f);
+                       continue;
+               }
+
                shared_ptr<Frame> primary_frame, secondary_frame;
                bool drop = false;
                for (int subframe_idx = 0; subframe_idx < 2; ++subframe_idx) {
-                       const JPEGID &id = (subframe_idx == 0 ? decode.primary : decode.secondary);
-                       if (id.stream_idx == (unsigned)-1) {
+                       const FrameOnDisk &frame_spec = (subframe_idx == 0 ? decode.primary : decode.secondary);
+                       if (frame_spec.pts == -1) {
                                // No secondary frame.
                                continue;
                        }
 
                        bool found_in_cache;
-                       shared_ptr<Frame> frame;
-                       if (id.interpolated) {
-                               // Interpolated frames are never decoded by us,
-                               // put directly into the cache from VideoStream.
-                               unique_lock<mutex> lock(cache_mu);
-                               auto it = cache.find(id);
-                               if (it != cache.end()) {
-                                       it->second.last_used = event_counter++;
-                                       frame = it->second.frame;
-                               } else {
-                                       // This can only really happen if it disappeared out of the
-                                       // LRU really, really fast. Which shouldn't happen.
-                                       fprintf(stderr, "WARNING: Interpolated JPEG was supposed to be in the cache, but was not\n");
-                               }
-                               found_in_cache = true;  // Don't count it as a decode.
-                       } else {
-                               frame = decode_jpeg_with_cache(id, 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(id.interpolated || cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE);
+                               assert(cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE);
                                drop = true;
                                break;
                        }
@@ -299,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) {
@@ -325,38 +323,28 @@ JPEGFrameView::JPEGFrameView(QWidget *parent)
 {
 }
 
-void JPEGFrameView::setFrame(unsigned stream_idx, int64_t pts, bool interpolated, int secondary_stream_idx, int64_t secondary_pts, float fade_alpha)
+void JPEGFrameView::setFrame(unsigned stream_idx, FrameOnDisk frame, FrameOnDisk secondary_frame, float fade_alpha)
 {
        current_stream_idx = stream_idx;  // TODO: Does this interact with fades?
 
        unique_lock<mutex> lock(cache_mu);
        PendingDecode decode;
-       if (interpolated && secondary_stream_idx != -1) {
-               // The frame will already be faded for us, so ask for only one; we shouldn't fade it against anything.
-               decode.primary = create_jpegid_for_interpolated_fade(stream_idx, pts, secondary_stream_idx, secondary_pts);
-               decode.secondary = JPEGID{ (unsigned)-1, -1, /*interpolated=*/false };
-       } else {
-               decode.primary = JPEGID{ stream_idx, pts, interpolated };
-               decode.secondary = JPEGID{ (unsigned)secondary_stream_idx, secondary_pts, /*interpolated=*/false };
-       }
+       decode.primary = frame;
+       decode.secondary = secondary_frame;
        decode.fade_alpha = fade_alpha;
        decode.destination = this;
        pending_decodes.push_back(decode);
        any_pending_decodes.notify_all();
 }
 
-void JPEGFrameView::insert_interpolated_frame(JPEGID id, shared_ptr<Frame> frame)
+void JPEGFrameView::setFrame(shared_ptr<Frame> frame)
 {
-       // We rely on the frame not being evicted from the cache before
-       // jpeg_decoder_thread() sees it and can display it (otherwise,
-       // that thread would hang). With a default cache of 1000 elements,
-       // that would sound like a reasonable assumption.
        unique_lock<mutex> lock(cache_mu);
-       cache_bytes_used += frame_size(*frame);
-       cache[id] = LRUFrame{ std::move(frame), event_counter++ };
-       if (cache_bytes_used > size_t(CACHE_SIZE_MB) * 1024 * 1024) {
-               prune_cache();
-       }
+       PendingDecode decode;
+       decode.frame = std::move(frame);
+       decode.destination = this;
+       pending_decodes.push_back(decode);
+       any_pending_decodes.notify_all();
 }
 
 ResourcePool *resource_pool = nullptr;