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