]> git.sesse.net Git - nageru/blob - player.cpp
Make it possible to switch camera angles for previews with the 1–4 keys.
[nageru] / player.cpp
1 #include <algorithm>
2 #include <chrono>
3 #include <condition_variable>
4 #include <mutex>
5 #include <thread>
6 #include <vector>
7
8 #include "clip_list.h"
9 #include "defs.h"
10 #include "jpeg_frame_view.h"
11 #include "player.h"
12
13 using namespace std;
14 using namespace std::chrono;
15
16 extern mutex frame_mu;
17 extern vector<int64_t> frames[MAX_STREAMS];
18
19 void Player::thread_func()
20 {
21         for ( ;; ) {
22                 // Wait until we're supposed to play something.
23                 {
24                         unique_lock<mutex> lock(queue_state_mu);
25                         new_clip_changed.wait(lock, [this]{
26                                 return new_clip_ready && current_clip.pts_in != -1;
27                         });
28                         new_clip_ready = false;
29                         playing = true;
30                 }
31
32                 Clip clip;
33                 unsigned stream_idx;
34                 {
35                         lock_guard<mutex> lock(mu);
36                         clip = current_clip;
37                         stream_idx = current_stream_idx;
38                 }
39                 steady_clock::time_point origin = steady_clock::now();
40                 int64_t pts_origin = clip.pts_in;
41
42                 int64_t next_pts = pts_origin - 1;  // Make sure we play the frame at clip.pts_in if it exists.
43
44                 bool aborted = false;
45                 for ( ;; ) {
46                         // Find the next frame.
47                         {
48                                 lock_guard<mutex> lock(frame_mu);
49                                 auto it = upper_bound(frames[stream_idx].begin(),
50                                         frames[stream_idx].end(),
51                                         next_pts);
52                                 if (it == frames[stream_idx].end() || *it >= clip.pts_out) {
53                                         break;
54                                 }
55                                 next_pts = *it;
56                         }
57
58                         // FIXME: assumes a given timebase.
59                         double speed = 0.5;
60                         steady_clock::time_point next_frame_start =
61                                 origin + microseconds((next_pts - pts_origin) * int(1000000 / speed) / 12800);
62
63                         // Sleep until the next frame start, or until there's a new clip we're supposed to play.
64                         {
65                                 unique_lock<mutex> lock(queue_state_mu);
66                                 new_clip_changed.wait_until(lock, next_frame_start, [this]{
67                                         return new_clip_ready || override_stream_idx != -1;
68                                 });
69                                 if (new_clip_ready) break;
70                                 if (override_stream_idx != -1) {
71                                         stream_idx = override_stream_idx;
72                                         override_stream_idx = -1;
73                                         continue;
74                                 }
75                         }
76
77                         destination->setFrame(stream_idx, next_pts);
78
79                 }
80
81                 {
82                         unique_lock<mutex> lock(queue_state_mu);
83                         playing = false;
84                 }
85                 if (done_callback != nullptr && !aborted) {
86                         done_callback();
87                 }
88         }
89 }
90
91 Player::Player(JPEGFrameView *destination)
92         : destination(destination)
93 {
94         thread(&Player::thread_func, this).detach();
95 }
96
97 void Player::play_clip(const Clip &clip, unsigned stream_idx)
98 {
99         {
100                 lock_guard<mutex> lock(mu);
101                 current_clip = clip;
102                 current_stream_idx = stream_idx;
103         }
104
105         {
106                 lock_guard<mutex> lock(queue_state_mu);
107                 new_clip_ready = true;
108                 override_stream_idx = -1;
109                 new_clip_changed.notify_all();
110         }
111 }
112
113 void Player::override_angle(unsigned stream_idx)
114 {
115         // Corner case: If a new clip is waiting to be played, change its stream and then we're done. 
116         {
117                 unique_lock<mutex> lock(queue_state_mu);
118                 if (new_clip_ready) {
119                         lock_guard<mutex> lock2(mu);
120                         current_stream_idx = stream_idx;
121                         return;
122                 }
123         }
124
125         // If we are playing a clip, set override_stream_idx, and the player thread will
126         // pick it up and change its internal index.
127         {
128                 unique_lock<mutex> lock(queue_state_mu);
129                 if (playing) {
130                         override_stream_idx = stream_idx;
131                         new_clip_changed.notify_all();
132                 }
133         }
134
135         // OK, so we're standing still, presumably at the end of a clip.
136         // Look at the current pts_out (if it exists), and show the closest
137         // thing we've got.
138         int64_t pts_out;
139         {
140                 lock_guard<mutex> lock(mu);
141                 if (current_clip.pts_out < 0) {
142                         return;
143                 }
144                 pts_out = current_clip.pts_out;
145         }
146                         
147         lock_guard<mutex> lock(frame_mu);
148         auto it = upper_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts_out);
149         if (it == frames[stream_idx].end()) {
150                 return;
151         }
152         destination->setFrame(stream_idx, *it);
153 }