1 #include "cef_encoder_adapter.h"
3 #include <bmusb/bmusb.h>
11 #include <libavutil/rational.h>
12 #include <libswscale/swscale.h>
16 #include "quittable_sleeper.h"
18 using namespace bmusb;
20 using namespace std::chrono;
24 // In kaeru.cpp. (This is a bit of a hack, but in the interest of a cleaner split.)
25 void video_frame_callback(FFmpegCapture *video, X264Encoder *x264_encoder, AudioEncoder *audio_encoder,
26 int64_t video_pts, AVRational video_timebase,
27 int64_t audio_pts, AVRational audio_timebase,
29 FrameAllocator::Frame video_frame, size_t video_offset, VideoFormat video_format,
30 FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format);
32 void CEFEncoderAdapter::video_frame_callback(uint16_t timecode,
33 FrameAllocator::Frame video_frame, size_t video_offset, VideoFormat video_format,
34 FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format)
38 start = steady_clock::now();
42 last_video_format = video_format;
43 last_audio_format = audio_format;
45 steady_clock::time_point now = steady_clock::now();
46 int64_t video_pts = duration_cast<microseconds>(now - start).count();
47 AVRational video_timebase{ 1, 1000000 };
49 FrameAllocator::Frame nv12_video_frame;
50 if (video_frame.len > 0) {
53 // Do the actual conversion, replacing the frame.
54 const uint8_t *src_pic_data[4] = { video_frame.data + video_offset, nullptr, nullptr, nullptr };
55 int src_linesizes[4] = { global_flags.width * 4, 0, 0, 0 };
57 uint8_t *pic_data[4] = { nv12_data.get(), nv12_data.get() + global_flags.width * global_flags.height, nullptr, nullptr };
58 int linesizes[4] = { global_flags.width, global_flags.width, 0, 0 };
60 sws_scale(sws_ctx.get(), src_pic_data, src_linesizes, 0, video_format.height, pic_data, linesizes);
62 nv12_video_frame.data = nv12_data.get();
63 nv12_video_frame.len = nv12_video_frame.size = global_flags.width * global_flags.height * 2;
64 if (video_frame.owner) {
65 video_frame.owner->release_frame(video_frame);
68 nv12_video_frame = video_frame;
69 // Will be released by video_frame_callback(), so don't do that here.
72 // NOTE: We don't have CEF audio yet.
74 ::video_frame_callback(nullptr, x264_encoder, audio_encoder,
75 video_pts, video_timebase,
76 /*audio_pts=*/0, AVRational{ 1, 1 },
78 nv12_video_frame, /*video_offset=*/0, video_format,
79 audio_frame, audio_offset, audio_format);
82 // Enforce at least 15 fps by duplicating frames. This feels suboptimal
83 // on the face of it, but so many things in x264 (in particular lookahead
84 // and keyframe interval) work on the number of frames, so not having
85 // frames for a while will mess up the latency pretty badly, especially
86 // when clients do the initial join on the stream.
88 // Returns early (without any duplication) if should_quit becomes active.
89 void CEFEncoderAdapter::duplicate_frame_if_needed(QuittableSleeper *should_quit)
91 constexpr duration max_frame_interval = milliseconds(1000 / 15);
93 optional<steady_clock::time_point> last_frame_now = get_last_frame();
94 if (!last_frame_now) {
95 // No initial frame yet, so nothing to duplicate. Just wait a bit.
96 should_quit->sleep_for(max_frame_interval);
99 steady_clock::time_point next_inserted_frame = *last_frame_now + max_frame_interval;
100 if (!should_quit->sleep_until(next_inserted_frame)) {
105 if (*last_frame_now != last_frame) {
106 // A new frame came while we were waiting, so we don't need one just yet.
110 // No new frame came before the deadline, so add a duplicate. The data from last
111 // conversion is still in the buffer, so we can just use that.
112 steady_clock::time_point now = steady_clock::now();
113 int64_t video_pts = duration_cast<microseconds>(now - start).count();
116 FrameAllocator::Frame nv12_video_frame;
117 nv12_video_frame.data = nv12_data.get();
118 nv12_video_frame.len = nv12_video_frame.size = global_flags.width * global_flags.height * 2;
119 ::video_frame_callback(nullptr, x264_encoder, audio_encoder,
120 video_pts, video_timebase,
121 /*audio_pts=*/0, AVRational{ 1, 1 },
123 nv12_video_frame, /*video_offset=*/0, last_video_format,
124 /*audio_frame=*/{}, /*audio_offset=*/0, last_audio_format);
127 optional<steady_clock::time_point> CEFEncoderAdapter::get_last_frame() const {