aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/VideoQueue.java
blob: a3c308f0024815f842fb7de4f29683158894bd5e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package invalid.lena.scrcpy;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

// Bounded encoded-video queue. It never leaves delta frames queued without
// the keyframe that starts their decoder generation.
final class VideoQueue {

    static final class Frame {
        final byte[] data;
        final long ptsUs;
        final boolean config;
        final boolean keyframe;

        Frame(byte[] data, long ptsUs, boolean config, boolean keyframe) {
            this.data = data;
            this.ptsUs = ptsUs;
            this.config = config;
            this.keyframe = keyframe;
        }
    }

    private final int maxFrames;
    private final int maxBytes;
    private final Deque<Frame> frames;
    private int bytes;
    private boolean needsKeyframe = true;

    VideoQueue(int maxFrames, int maxBytes) {
        if (maxFrames < 1 || maxBytes < 1) throw new IllegalArgumentException();
        this.maxFrames = maxFrames;
        this.maxBytes = maxBytes;
        frames = new ArrayDeque<>(maxFrames);
    }

    boolean offer(Frame frame) {
        if (frame.config) {
            clear();
            return append(frame);
        }
        if (frame.keyframe) {
            removeMediaFrames();
            needsKeyframe = false;
            if (append(frame)) return true;
            needsKeyframe = true;
            return false;
        }
        if (needsKeyframe) return false;
        if (append(frame)) return true;
        needsKeyframe = true;
        return false;
    }

    Frame poll() {
        Frame frame = frames.pollFirst();
        if (frame != null) bytes -= frame.data.length;
        return frame;
    }

    boolean isEmpty() {
        return frames.isEmpty();
    }

    boolean needsKeyframe() {
        return needsKeyframe;
    }

    void clear() {
        frames.clear();
        bytes = 0;
        needsKeyframe = true;
    }

    private boolean append(Frame frame) {
        if (frames.size() >= maxFrames || frame.data.length > maxBytes - bytes) {
            return false;
        }
        frames.offerLast(frame);
        bytes += frame.data.length;
        return true;
    }

    private void removeMediaFrames() {
        for (Iterator<Frame> it = frames.iterator(); it.hasNext(); ) {
            Frame frame = it.next();
            if (!frame.config) {
                it.remove();
                bytes -= frame.data.length;
            }
        }
    }
}