3 #include <condition_variable>
11 #include <libavformat/avformat.h>
12 #include <libavformat/avio.h>
15 #include "clip_list.h"
17 #include "ffmpeg_raii.h"
19 #include "jpeg_frame_view.h"
24 using namespace std::chrono;
26 extern mutex frame_mu;
27 extern vector<int64_t> frames[MAX_STREAMS];
28 extern HTTPD *global_httpd;
32 string read_file(const string &filename)
34 FILE *fp = fopen(filename.c_str(), "rb");
36 perror(filename.c_str());
40 fseek(fp, 0, SEEK_END);
46 fread(&ret[0], len, 1, fp);
53 void Player::thread_func()
56 // Wait until we're supposed to play something.
58 unique_lock<mutex> lock(queue_state_mu);
59 new_clip_changed.wait(lock, [this]{
60 return new_clip_ready && current_clip.pts_in != -1;
62 new_clip_ready = false;
69 lock_guard<mutex> lock(mu);
71 stream_idx = current_stream_idx;
73 steady_clock::time_point origin = steady_clock::now();
74 int64_t pts_origin = clip.pts_in;
76 int64_t next_pts = pts_origin - 1; // Make sure we play the frame at clip.pts_in if it exists.
80 // Find the next frame.
82 lock_guard<mutex> lock(frame_mu);
83 auto it = upper_bound(frames[stream_idx].begin(),
84 frames[stream_idx].end(),
86 if (it == frames[stream_idx].end() || *it >= clip.pts_out) {
92 // FIXME: assumes a given timebase.
94 steady_clock::time_point next_frame_start =
95 origin + microseconds((next_pts - pts_origin) * int(1000000 / speed) / 12800);
97 // Sleep until the next frame start, or until there's a new clip we're supposed to play.
99 unique_lock<mutex> lock(queue_state_mu);
100 new_clip_changed.wait_until(lock, next_frame_start, [this]{
101 return new_clip_ready || override_stream_idx != -1;
103 if (new_clip_ready) break;
104 if (override_stream_idx != -1) {
105 stream_idx = override_stream_idx;
106 override_stream_idx = -1;
111 destination->setFrame(stream_idx, next_pts);
113 // Send the frame to the stream.
114 // FIXME: Vaguely less crazy pts, perhaps.
115 double pts_float = fmod(duration<double>(next_frame_start.time_since_epoch()).count(), 86400.0f);
116 int64_t pts = lrint(pts_float * TIMEBASE);
117 string jpeg = read_file(filename_for_frame(stream_idx, next_pts));
119 av_init_packet(&pkt);
120 pkt.stream_index = 0;
121 pkt.data = (uint8_t *)jpeg.data();
122 pkt.size = jpeg.size();
123 stream_mux->add_packet(pkt, pts, pts);
127 unique_lock<mutex> lock(queue_state_mu);
130 if (done_callback != nullptr && !aborted) {
136 Player::Player(JPEGFrameView *destination)
137 : destination(destination)
139 open_output_stream();
140 thread(&Player::thread_func, this).detach();
143 void Player::play_clip(const Clip &clip, unsigned stream_idx)
146 lock_guard<mutex> lock(mu);
148 current_stream_idx = stream_idx;
152 lock_guard<mutex> lock(queue_state_mu);
153 new_clip_ready = true;
154 override_stream_idx = -1;
155 new_clip_changed.notify_all();
159 void Player::override_angle(unsigned stream_idx)
161 // Corner case: If a new clip is waiting to be played, change its stream and then we're done.
163 unique_lock<mutex> lock(queue_state_mu);
164 if (new_clip_ready) {
165 lock_guard<mutex> lock2(mu);
166 current_stream_idx = stream_idx;
171 // If we are playing a clip, set override_stream_idx, and the player thread will
172 // pick it up and change its internal index.
174 unique_lock<mutex> lock(queue_state_mu);
176 override_stream_idx = stream_idx;
177 new_clip_changed.notify_all();
181 // OK, so we're standing still, presumably at the end of a clip.
182 // Look at the current pts_out (if it exists), and show the closest
186 lock_guard<mutex> lock(mu);
187 if (current_clip.pts_out < 0) {
190 pts_out = current_clip.pts_out;
193 lock_guard<mutex> lock(frame_mu);
194 auto it = upper_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts_out);
195 if (it == frames[stream_idx].end()) {
198 destination->setFrame(stream_idx, *it);
201 void Player::open_output_stream()
203 AVFormatContext *avctx = avformat_alloc_context();
204 avctx->oformat = av_guess_format("nut", nullptr, nullptr);
206 uint8_t *buf = (uint8_t *)av_malloc(MUX_BUFFER_SIZE);
207 avctx->pb = avio_alloc_context(buf, MUX_BUFFER_SIZE, 1, this, nullptr, nullptr, nullptr);
208 avctx->pb->write_data_type = &Player::write_packet2_thunk;
209 avctx->pb->ignore_boundary_point = 1;
211 Mux::Codec video_codec = Mux::CODEC_MJPEG;
213 avctx->flags = AVFMT_FLAG_CUSTOM_IO;
215 string video_extradata;
217 constexpr int width = 1280, height = 720; // Doesn't matter for MJPEG.
218 stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, /*audio_codec_parameters=*/nullptr, COARSE_TIMEBASE,
219 /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {}));
222 int Player::write_packet2_thunk(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time)
224 Player *player = (Player *)opaque;
225 return player->write_packet2(buf, buf_size, type, time);
228 int Player::write_packet2(uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time)
230 if (type == AVIO_DATA_MARKER_SYNC_POINT || type == AVIO_DATA_MARKER_BOUNDARY_POINT) {
231 seen_sync_markers = true;
232 } else if (type == AVIO_DATA_MARKER_UNKNOWN && !seen_sync_markers) {
233 // We don't know if this is a keyframe or not (the muxer could
234 // avoid marking it), so we just have to make the best of it.
235 type = AVIO_DATA_MARKER_SYNC_POINT;
238 if (type == AVIO_DATA_MARKER_HEADER) {
239 stream_mux_header.append((char *)buf, buf_size);
240 global_httpd->set_header(stream_mux_header);
242 global_httpd->add_data((char *)buf, buf_size, type == AVIO_DATA_MARKER_SYNC_POINT, time, AVRational{ AV_TIME_BASE, 1 });