20#include <condition_variable>
34 #include <libavdevice/avdevice.h>
35 #include <libavutil/imgutils.h>
36 #include <libavutil/time.h>
43 #include <audioclient.h>
44 #include <mmdeviceapi.h>
53#if defined(HAVE_WAYLAND_CAPTURE_PLUGIN) && defined(__linux__)
56 using WaylandBackendFactory = std::unique_ptr<ScreenCaptureReader::CaptureBackendReader> (*) (
59 void* load_wayland_capture_backend(std::string& error_message)
61 Dl_info library_info {};
62 if (dladdr(
reinterpret_cast<void*
>(load_wayland_capture_backend), &library_info)
63 && library_info.dli_fname) {
64 std::string library_path(library_info.dli_fname);
65 const auto separator = library_path.find_last_of(
'/');
66 if (separator != std::string::npos) {
67 const std::string module_path = library_path.substr(0, separator + 1)
68 +
"libopenshot-wayland-capture.so";
69 if (
void* module = dlopen(module_path.c_str(), RTLD_NOW | RTLD_LOCAL)) {
72 const char* error = dlerror();
73 error_message = error ? error :
"unable to load PipeWire support.";
77 void*
module = dlopen("libopenshot-wayland-capture.so", RTLD_NOW | RTLD_LOCAL);
79 const char* error = dlerror();
80 error_message = error ? error :
"unable to load PipeWire support.";
92 : settings(new_settings) {}
94 ~SystemAudioCapture() {
Close(); }
98 if (format_context)
return;
99 avdevice_register_all();
100 AVInputFormat* input_format =
const_cast<AVInputFormat*
>(av_find_input_format(
"pulse"));
102 throw InvalidOptions(
"FFmpeg PulseAudio input is unavailable for system audio capture.");
105 format_context = avformat_alloc_context();
106 if (!format_context) {
107 throw OutOfMemory(
"Unable to allocate system audio capture context.");
109 format_context->interrupt_callback.callback = InterruptCallback;
110 format_context->interrupt_callback.opaque = &close_requested;
112 AVDictionary* options =
nullptr;
113 av_dict_set(&options,
"wallclock",
"1", 0);
117 av_dict_set_int(&options,
"fragment_size",
118 static_cast<int64_t
>(settings.audio_sample_rate) * settings.audio_channels * 2 / 50, 0);
119 av_dict_set(&options,
"sample_rate", std::to_string(settings.audio_sample_rate).c_str(), 0);
120 av_dict_set(&options,
"channels", std::to_string(settings.audio_channels).c_str(), 0);
121 const std::string device = settings.audio_device.empty() ?
"@DEFAULT_MONITOR@" : settings.audio_device;
122 const int open_result = avformat_open_input(&format_context, device.c_str(), input_format, &options);
123 av_dict_free(&options);
124 if (open_result < 0) {
125 throw InvalidFile(
"Unable to open system audio capture input: " + std::string(
av_err2str(open_result)), device);
127 if (avformat_find_stream_info(format_context,
nullptr) < 0) {
128 throw InvalidFile(
"Unable to read system audio capture stream information.", device);
130 for (
unsigned int index = 0; index < format_context->nb_streams; ++index) {
131 if (format_context->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
132 audio_stream =
static_cast<int>(index);
136 if (audio_stream < 0) {
137 throw InvalidFile(
"No audio stream found in system audio capture input.", device);
139 AVStream* stream = format_context->streams[audio_stream];
140 const AVCodec*
codec = avcodec_find_decoder(stream->codecpar->codec_id);
141 codec_context =
codec ? ffmpeg_get_codec_context(stream,
codec) : nullptr;
142 if (codec_context) codec_context->pkt_timebase = stream->time_base;
143 if (!codec_context || avcodec_open2(codec_context,
codec,
nullptr) < 0) {
144 throw InvalidCodec(
"Unable to open system audio capture decoder.", device);
146 packet = av_packet_alloc();
147 decoded_frame = av_frame_alloc();
148 if (!packet || !decoded_frame) {
149 throw OutOfMemory(
"Unable to allocate system audio capture buffers.");
151 settings.audio_sample_rate = codec_context->sample_rate > 0
152 ? codec_context->sample_rate : settings.audio_sample_rate;
154 close_requested =
false;
155 worker = std::thread(&SystemAudioCapture::CaptureLoop,
this);
160 close_requested =
true;
162 if (worker.joinable()) {
165 if (decoded_frame) av_frame_free(&decoded_frame);
166 if (packet) av_packet_free(&packet);
167 if (codec_context) avcodec_free_context(&codec_context);
168 if (format_context) avformat_close_input(&format_context);
173 int sample_count = 0;
174 for (int64_t frame_number = last_output_frame + 1; frame_number <= number; ++frame_number) {
176 frame_number, fps, settings.audio_sample_rate, settings.audio_channels);
178 last_output_frame = std::max(last_output_frame, number);
179 if (!frame || sample_count <= 0)
return;
181 std::unique_lock<std::mutex> lock(queue_mutex);
184 const auto wait_time = timeline_started
185 ? std::chrono::milliseconds(100)
186 : std::chrono::milliseconds(3000);
187 ready.wait_for(lock, wait_time, [
this, sample_count]() {
188 return close_requested || audio_buffer.Covers(sample_count);
190 timeline_started =
true;
191 auto samples = audio_buffer.Read(sample_count);
192 frame->SampleRate(settings.audio_sample_rate);
194 for (
int channel = 0; channel < settings.audio_channels; ++channel) {
195 frame->AddAudio(
true, channel, 0, samples[channel].data(), sample_count, 1.0f);
201 std::lock_guard<std::mutex> lock(queue_mutex);
204 epoch_us = av_gettime();
205 audio_buffer.Reset(settings.audio_channels,
static_cast<int64_t
>(settings.audio_sample_rate) * 10);
206 last_output_frame = 0;
207 timeline_started =
false;
210 int SampleRate()
const {
return settings.audio_sample_rate; }
211 int Channels()
const {
return settings.audio_channels; }
214 static int InterruptCallback(
void* opaque)
216 const auto* requested =
static_cast<std::atomic<bool>*
>(opaque);
217 return requested && requested->load() ? 1 : 0;
220 float SampleAt(
const AVFrame* frame,
int channel,
int sample)
const
222 const AVSampleFormat format =
static_cast<AVSampleFormat
>(frame->format);
223 const bool planar = av_sample_fmt_is_planar(format) != 0;
224 const int source_channels = std::max(1,
226 codec_context->ch_layout.nb_channels
228 codec_context->channels
231 const int source_channel = std::min(channel, source_channels - 1);
232 const uint8_t* data = planar ? frame->extended_data[source_channel] : frame->extended_data[0];
233 const int offset = planar ? sample : sample * source_channels + source_channel;
235 case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
236 return (
static_cast<const uint8_t*
>(
static_cast<const void*
>(data))[offset] - 128) / 128.0f;
237 case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
238 return static_cast<const int16_t*
>(
static_cast<const void*
>(data))[offset] / 32768.0f;
239 case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
240 return static_cast<float>(
static_cast<const int32_t*
>(
static_cast<const void*
>(data))[offset] / 2147483648.0);
241 case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
242 return static_cast<const float*
>(
static_cast<const void*
>(data))[offset];
243 case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP:
244 return static_cast<float>(
static_cast<const double*
>(
static_cast<const void*
>(data))[offset]);
252 while (!close_requested) {
253 const int read_result = av_read_frame(format_context, packet);
254 if (read_result == AVERROR(EAGAIN))
continue;
255 if (read_result < 0)
break;
256 if (packet->stream_index != audio_stream) {
257 av_packet_unref(packet);
260 const int send_result = avcodec_send_packet(codec_context, packet);
261 av_packet_unref(packet);
262 if (send_result < 0)
continue;
263 while (avcodec_receive_frame(codec_context, decoded_frame) == 0) {
264 const int64_t timestamp = decoded_frame->best_effort_timestamp != AV_NOPTS_VALUE
265 ? decoded_frame->best_effort_timestamp : decoded_frame->pts;
266 if (timestamp == AV_NOPTS_VALUE) {
268 av_frame_unref(decoded_frame);
271 const int64_t timestamp_us = av_rescale_q(timestamp,
272 format_context->streams[audio_stream]->time_base, AVRational{1, AV_TIME_BASE});
273 std::vector<std::vector<float>> samples(settings.audio_channels,
274 std::vector<float>(decoded_frame->nb_samples));
275 for (
int channel = 0; channel < settings.audio_channels; ++channel) {
276 for (
int sample = 0; sample < decoded_frame->nb_samples; ++sample) {
277 samples[channel][sample] = SampleAt(decoded_frame, channel, sample);
280 std::lock_guard<std::mutex> lock(queue_mutex);
281 audio_buffer.PushTimestamp(timestamp_us, epoch_us, AVRational{1, AV_TIME_BASE},
282 settings.audio_sample_rate, std::move(samples));
283 av_frame_unref(decoded_frame);
290 AVFormatContext* format_context =
nullptr;
291 AVCodecContext* codec_context =
nullptr;
292 AVPacket* packet =
nullptr;
293 AVFrame* decoded_frame =
nullptr;
294 int audio_stream = -1;
295 std::atomic<bool> close_requested {
false };
297 std::mutex queue_mutex;
298 std::condition_variable ready;
300 int64_t epoch_us = 0;
301 int64_t last_output_frame = 0;
302 bool timeline_started =
false;
309 : settings(new_settings) {}
311 ~SystemAudioCapture() {
Close(); }
315 if (worker.joinable())
return;
316 close_requested =
false;
317 worker = std::thread(&SystemAudioCapture::CaptureLoop,
this);
318 std::unique_lock<std::mutex> lock(state_mutex);
319 state_ready.wait_for(lock, std::chrono::seconds(5), [
this]() {
return opened || failed; });
321 close_requested =
true;
323 if (worker.joinable()) worker.join();
324 throw InvalidOptions(error.empty() ?
"Unable to open WASAPI system audio loopback capture." : error);
330 close_requested =
true;
332 if (worker.joinable()) worker.join();
338 int sample_count = 0;
339 for (int64_t frame_number = last_output_frame + 1; frame_number <= number; ++frame_number) {
342 last_output_frame = std::max(last_output_frame, number);
343 if (!frame || sample_count <= 0)
return;
344 std::unique_lock<std::mutex> lock(queue_mutex);
345 const auto wait_time = timeline_started
346 ? std::chrono::milliseconds(100)
347 : std::chrono::milliseconds(3000);
348 ready.wait_for(lock, wait_time, [
this, sample_count]() {
349 return close_requested || audio_buffer.Covers(sample_count);
351 timeline_started =
true;
352 auto samples = audio_buffer.Read(sample_count);
353 frame->SampleRate(sample_rate);
355 for (
int channel = 0; channel < channel_count; ++channel) {
356 frame->AddAudio(
true, channel, 0, samples[channel].data(), sample_count, 1.0f);
362 std::lock_guard<std::mutex> lock(queue_mutex);
364 LARGE_INTEGER counter, frequency;
365 QueryPerformanceCounter(&counter);
366 QueryPerformanceFrequency(&frequency);
367 epoch_100ns = av_rescale(counter.QuadPart, 10000000, frequency.QuadPart);
368 audio_buffer.Reset(channel_count,
static_cast<int64_t
>(sample_rate) * 10);
369 last_output_frame = 0;
370 timeline_started =
false;
373 int SampleRate()
const {
return sample_rate; }
374 int Channels()
const {
return channel_count; }
377 template <
typename T>
static void Release(T*& value)
379 if (value) { value->Release(); value =
nullptr; }
382 void Fail(
const std::string& message)
384 std::lock_guard<std::mutex> lock(state_mutex);
387 state_ready.notify_all();
392 IMMDeviceEnumerator* enumerator =
nullptr;
393 IMMDevice* device =
nullptr;
394 IAudioClient* client =
nullptr;
395 IAudioCaptureClient* capture =
nullptr;
396 WAVEFORMATEX* format =
nullptr;
397 const HRESULT com_result = CoInitializeEx(
nullptr, COINIT_MULTITHREADED);
398 const bool uninitialize_com = SUCCEEDED(com_result);
399 HRESULT result = CoCreateInstance(CLSID_MMDeviceEnumerator,
nullptr, CLSCTX_ALL,
400 IID_IMMDeviceEnumerator,
reinterpret_cast<void**
>(&enumerator));
401 if (SUCCEEDED(result)) {
402 result = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
404 if (SUCCEEDED(result)) {
405 result = device->Activate(IID_IAudioClient, CLSCTX_ALL,
nullptr,
reinterpret_cast<void**
>(&client));
407 if (SUCCEEDED(result)) result = client->GetMixFormat(&format);
408 if (SUCCEEDED(result)) {
409 result = client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK,
410 0, 0, format,
nullptr);
412 if (SUCCEEDED(result)) {
413 result = client->GetService(IID_IAudioCaptureClient,
reinterpret_cast<void**
>(&capture));
415 if (SUCCEEDED(result)) result = client->Start();
416 if (FAILED(result) || !format || !capture) {
417 Fail(
"Unable to initialize WASAPI system audio loopback capture (HRESULT " + std::to_string(result) +
").");
418 if (format) CoTaskMemFree(format);
419 Release(capture); Release(client); Release(device); Release(enumerator);
420 if (uninitialize_com) CoUninitialize();
424 sample_rate =
static_cast<int>(format->nSamplesPerSec);
425 channel_count = std::max(1, std::min(2,
static_cast<int>(format->nChannels)));
427 bool floating_point = format->wFormatTag == WAVE_FORMAT_IEEE_FLOAT;
428 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= 22) {
429 const auto* extensible =
reinterpret_cast<const WAVEFORMATEXTENSIBLE*
>(format);
430 floating_point = extensible->SubFormat.Data1 == WAVE_FORMAT_IEEE_FLOAT;
433 std::lock_guard<std::mutex> lock(state_mutex);
435 state_ready.notify_all();
438 while (!close_requested) {
439 UINT32 packet_frames = 0;
440 if (FAILED(capture->GetNextPacketSize(&packet_frames)))
break;
441 if (packet_frames == 0) {
442 std::this_thread::sleep_for(std::chrono::milliseconds(2));
445 BYTE* data =
nullptr;
448 UINT64 timestamp_100ns = 0;
449 if (FAILED(capture->GetBuffer(&data, &frames, &flags,
nullptr, ×tamp_100ns)))
break;
451 std::vector<std::vector<float>> samples(channel_count, std::vector<float>(frames));
452 for (UINT32 frame_index = 0; frame_index < frames; ++frame_index) {
453 for (
int channel = 0; channel < channel_count; ++channel) {
455 if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT) && data) {
456 const int offset =
static_cast<int>(frame_index) * format->nChannels + channel;
457 if (floating_point && format->wBitsPerSample == 32) {
458 sample =
reinterpret_cast<const float*
>(data)[offset];
459 }
else if (format->wBitsPerSample == 16) {
460 sample =
reinterpret_cast<const int16_t*
>(data)[offset] / 32768.0f;
461 }
else if (format->wBitsPerSample == 32) {
462 sample =
static_cast<float>(
reinterpret_cast<const int32_t*
>(data)[offset] / 2147483648.0);
465 samples[channel][frame_index] = sample;
470 if (!(flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR)) {
471 std::lock_guard<std::mutex> lock(queue_mutex);
472 audio_buffer.PushTimestamp(
static_cast<int64_t
>(timestamp_100ns), epoch_100ns,
473 AVRational{1, 10000000}, sample_rate, std::move(samples));
476 capture->ReleaseBuffer(frames);
481 CoTaskMemFree(format);
482 Release(capture); Release(client); Release(device); Release(enumerator);
483 if (uninitialize_com) CoUninitialize();
487 std::atomic<bool> close_requested {
false };
489 std::mutex queue_mutex;
490 std::condition_variable ready;
492 int64_t epoch_100ns = 0;
493 int64_t last_output_frame = 0;
494 bool timeline_started =
false;
495 int sample_rate = 48000;
496 int channel_count = 2;
497 std::mutex state_mutex;
498 std::condition_variable state_ready;
519 std::string fraction_to_string(
const Fraction& value)
521 std::stringstream stream;
522 stream << value.
num <<
"/" << value.
den;
526 void set_option(AVDictionary** options,
const std::string& key,
const std::string& value)
528 av_dict_set(options, key.c_str(), value.c_str(), 0);
531 bool option_enabled(
const std::map<std::string, std::string>& options,
const std::string& key)
533 const auto option = options.find(key);
534 if (option == options.end()) {
537 return option->second ==
"1" || option->second ==
"true" || option->second ==
"yes";
540 int option_int(
const std::map<std::string, std::string>& options,
const std::string& key,
int fallback)
542 const auto option = options.find(key);
543 if (option == options.end()) {
547 return std::stoi(option->second);
553 int capture_interrupt_callback(
void* opaque)
555 const auto* reader =
static_cast<std::atomic<bool>*
>(opaque);
556 return reader && reader->load() ? 1 : 0;
559 std::string avfoundation_video_name(
const std::string& input_name)
561 const size_t separator = input_name.find(
':');
562 return separator == std::string::npos ? input_name : input_name.substr(0, separator);
565 std::string avfoundation_audio_name(
const std::string& input_name)
567 const size_t separator = input_name.find(
':');
568 return separator == std::string::npos ?
"none" : input_name.substr(separator + 1);
571 bool resolve_avfoundation_video_index(
572 AVInputFormat* input_format,
573 const std::string& video_name,
574 std::string& video_index)
576 const auto is_digit = [](
unsigned char value) {
577 return std::isdigit(value) != 0;
579 if (video_name.empty()
580 || video_name ==
"default"
581 || video_name ==
"none"
582 || std::all_of(video_name.begin(), video_name.end(), is_digit)) {
586 AVDeviceInfoList* device_list =
nullptr;
587 const int result = avdevice_list_input_sources(input_format,
nullptr,
nullptr, &device_list);
588 if (result < 0 || !device_list) {
589 avdevice_free_list_devices(&device_list);
594 for (
int index = 0; index < device_list->nb_devices; ++index) {
595 const AVDeviceInfo* device = device_list->devices[index];
596 if (!device || !device->device_name) {
599 const std::string name = device->device_name;
600 const std::string description = device->device_description
601 ? device->device_description
602 : device->device_name;
603 if (video_name == name || video_name == description) {
604 video_index = std::all_of(name.begin(), name.end(), is_digit)
606 : std::to_string(index);
611 avdevice_free_list_devices(&device_list);
617 : settings(new_settings)
618 , backend_reader(nullptr)
619 , backend_module(nullptr)
624 , format_context(nullptr)
625 , codec_context(nullptr)
626 , source_frame(nullptr)
627 , rgba_frame(nullptr)
629 , sws_context(nullptr)
630 , close_requested(false)
631 , system_audio(nullptr)
638#if defined(__linux__) || defined(_WIN32)
640 system_audio = std::make_unique<SystemAudioCapture>(settings);
643 if (UsesWaylandPortal()) {
644 #if defined(HAVE_WAYLAND_CAPTURE_PLUGIN) && defined(__linux__)
645 std::string module_error;
646 backend_module = load_wayland_capture_backend(module_error);
647 if (!backend_module) {
648 throw InvalidOptions(
"Wayland screen capture backend is unavailable: "
651 auto factory =
reinterpret_cast<WaylandBackendFactory
>(
652 dlsym(backend_module,
"OpenShotCreateWaylandScreenCaptureReader"));
654 const char* error = dlerror();
655 dlclose(backend_module);
656 backend_module =
nullptr;
657 throw InvalidOptions(
"Wayland screen capture backend is invalid: "
658 + std::string(error ? error :
"factory function is missing."));
661 backend_reader = factory(settings,
info);
663 dlclose(backend_module);
664 backend_module =
nullptr;
667 if (!backend_reader) {
668 dlclose(backend_module);
669 backend_module =
nullptr;
670 throw InvalidOptions(
"Wayland screen capture backend is unavailable.");
673 throw InvalidOptions(
"Wayland screen capture backend is unavailable in this build.");
681#if defined(__linux__)
682 backend_reader.reset();
683 if (backend_module) {
684 dlclose(backend_module);
685 backend_module =
nullptr;
692 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
693 return backend_reader ? backend_reader->IsOpen() : is_open;
698#if defined(__linux__)
702#if defined(HAVE_WAYLAND_CAPTURE_PLUGIN)
710#elif defined(__APPLE__)
720#if defined(__linux__)
724 avdevice_register_all();
726 && av_find_input_format(
"pulse") !=
nullptr;
737#if defined(__linux__)
738 const char* session = std::getenv(
"XDG_SESSION_TYPE");
742 if (!session || std::string(session) ==
"x11") {
747#elif defined(__APPLE__)
753void ScreenCaptureReader::ValidateSettings()
const
756 throw InvalidOptions(
"Screen capture backend is not supported on this OS or session.");
758 if (!UsesFFmpegDevice() && !UsesWaylandPortal()) {
759 throw InvalidOptions(
"Screen capture backend is not implemented in this build.");
762 throw InvalidOptions(
"Screen capture requires a positive width and height.");
765 throw InvalidOptions(
"Screen capture requires a positive frame rate.");
768 throw InvalidOptions(
"System audio capture is not supported by this screen capture backend.");
771 throw InvalidSampleRate(
"System audio capture requires a sample rate of at least 8000 Hz.");
774 throw InvalidChannels(
"System audio capture requires one or two channels.");
778bool ScreenCaptureReader::UsesFFmpegDevice()
const
783 || settings.
options.count(
"input_format_name");
786bool ScreenCaptureReader::UsesWaylandPortal()
const
791std::string ScreenCaptureReader::InputFormatName()
const
793 const auto override_format = settings.
options.find(
"input_format_name");
794 if (override_format != settings.
options.end()) {
795 return override_format->second;
804 return "avfoundation";
809std::string ScreenCaptureReader::InputName()
const
811 if (InputFormatName() ==
"v4l2") {
812 return settings.
display.empty() ?
"/dev/video0" : settings.
display;
814 if (InputFormatName() ==
"dshow") {
817 if (InputFormatName() ==
"gdigrab") {
820 if (InputFormatName() ==
"avfoundation") {
821 return settings.
display.empty() ?
"Capture screen 0:none" : settings.
display;
824 std::string display = settings.
display;
825 if (display.empty()) {
826 const char* env_display = std::getenv(
"DISPLAY");
827 display = env_display ? env_display :
":0.0";
829 if (InputFormatName() ==
"x11grab" && settings.
options.count(
"window_id")) {
833 std::stringstream input;
834 input << display <<
"+" << settings.
x <<
"," << settings.
y;
838void ScreenCaptureReader::PopulateInfo()
870 if (backend_reader) {
871 if (backend_reader->IsOpen())
return;
872 manual_system_audio =
false;
874 system_audio->Open();
880 backend_reader->Open();
882 if (system_audio) system_audio->Close();
890 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
894 manual_system_audio =
false;
895 close_requested =
false;
898 system_audio->Open();
905 if (close_requested) {
906 throw ReaderClosed(
"Capture initialization was cancelled.");
917void ScreenCaptureReader::OpenDevice()
919 avdevice_register_all();
921 AVInputFormat* input_format =
const_cast<AVInputFormat*
>(av_find_input_format(InputFormatName().c_str()));
922 std::string input_name = InputName();
924 throw InvalidOptions(
"FFmpeg input device is not available: " + InputFormatName(), input_name);
927 format_context = avformat_alloc_context();
928 if (!format_context) {
929 throw OutOfMemory(
"Unable to allocate capture format context.", input_name);
931 format_context->interrupt_callback.callback = capture_interrupt_callback;
932 format_context->interrupt_callback.opaque = &close_requested;
933 if (InputFormatName() ==
"dshow") {
936 format_context->flags |= AVFMT_FLAG_NONBLOCK;
939 AVDictionary* options =
nullptr;
940 const bool use_device_defaults = settings.
options.count(
"use_device_defaults") > 0;
941 const bool post_capture_crop = option_enabled(settings.
options,
"crop_after_capture");
942 if (!use_device_defaults && !post_capture_crop) {
943 set_option(&options,
"framerate", fraction_to_string(settings.
fps));
944 set_option(&options,
"video_size", std::to_string(settings.
width) +
"x" + std::to_string(settings.
height));
945 }
else if (post_capture_crop) {
946 const int source_width = option_int(settings.
options,
"crop_source_width", settings.
width);
947 const int source_height = option_int(settings.
options,
"crop_source_height", settings.
height);
948 set_option(&options,
"framerate", fraction_to_string(settings.
fps));
949 set_option(&options,
"video_size", std::to_string(source_width) +
"x" + std::to_string(source_height));
951 if (InputFormatName() ==
"x11grab") {
952 set_option(&options,
"draw_mouse", settings.
include_cursor ?
"1" :
"0");
953 set_option(&options,
"show_region", settings.
show_region ?
"1" :
"0");
954 }
else if (InputFormatName() ==
"gdigrab") {
955 set_option(&options,
"draw_mouse", settings.
include_cursor ?
"1" :
"0");
956 set_option(&options,
"show_region", settings.
show_region ?
"1" :
"0");
957 set_option(&options,
"offset_x", std::to_string(settings.
x));
958 set_option(&options,
"offset_y", std::to_string(settings.
y));
959 }
else if (InputFormatName() ==
"avfoundation") {
960 set_option(&options,
"capture_cursor", settings.
include_cursor ?
"1" :
"0");
961 std::string video_index;
962 if (resolve_avfoundation_video_index(input_format, avfoundation_video_name(input_name), video_index)) {
963 set_option(&options,
"video_device_index", video_index);
964 input_name =
":" + avfoundation_audio_name(input_name);
967 for (
const auto& option : settings.options) {
968 if (option.first ==
"input_format_name"
969 || option.first ==
"use_device_defaults"
970 || option.first ==
"crop_after_capture"
971 || option.first ==
"crop_source_width"
972 || option.first ==
"crop_source_height") {
975 set_option(&options, option.first, option.second);
978 const int result = avformat_open_input(&format_context, input_name.c_str(), input_format, &options);
979 av_dict_free(&options);
981 throw InvalidFile(
"Unable to open screen capture input: " + std::string(
av_err2str(result)), input_name);
985void ScreenCaptureReader::OpenDecoder()
987 if (avformat_find_stream_info(format_context,
nullptr) < 0) {
988 throw InvalidFile(
"Unable to read capture stream information.", InputName());
991 for (
unsigned int index = 0; index < format_context->nb_streams; ++index) {
992 if (format_context->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
993 video_stream =
static_cast<int>(index);
997 if (video_stream < 0) {
998 throw InvalidFile(
"No video stream found in capture input.", InputName());
1001 AVStream* stream = format_context->streams[video_stream];
1002 const AVCodec*
codec = avcodec_find_decoder(stream->codecpar->codec_id);
1004 throw InvalidCodec(
"No decoder available for capture stream.", InputName());
1007 codec_context = ffmpeg_get_codec_context(stream,
codec);
1008 if (!codec_context || avcodec_open2(codec_context,
codec,
nullptr) < 0) {
1009 throw InvalidCodec(
"Unable to open capture stream decoder.", InputName());
1012 info.
width = codec_context->width > 0 ? codec_context->width : settings.
width;
1013 info.
height = codec_context->height > 0 ? codec_context->height : settings.
height;
1014 if (option_enabled(settings.
options,
"crop_after_capture")) {
1022 if (codec_context->framerate.num > 0 && codec_context->framerate.den > 0) {
1023 info.
fps =
Fraction(codec_context->framerate.num, codec_context->framerate.den);
1025 }
else if (stream->avg_frame_rate.num > 0 && stream->avg_frame_rate.den > 0) {
1026 info.
fps =
Fraction(stream->avg_frame_rate.num, stream->avg_frame_rate.den);
1028 }
else if (stream->r_frame_rate.num > 0 && stream->r_frame_rate.den > 0) {
1029 info.
fps =
Fraction(stream->r_frame_rate.num, stream->r_frame_rate.den);
1033 source_frame = av_frame_alloc();
1034 rgba_frame = av_frame_alloc();
1035 packet = av_packet_alloc();
1036 if (!source_frame || !rgba_frame || !packet) {
1037 throw OutOfMemory(
"Unable to allocate capture decoder frames.", InputName());
1043 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1044 if (backend_reader) {
1045 if (!backend_reader->IsOpen()) {
1046 throw ReaderClosed(
"The ScreenCaptureReader is closed. Call Open() before GetFrame().");
1048 auto frame = backend_reader->GetFrame(number);
1049 if (system_audio && !manual_system_audio) system_audio->AddFrameAudio(frame, number,
info.
fps);
1053 throw ReaderClosed(
"The ScreenCaptureReader is closed. Call Open() before GetFrame().");
1056 auto frame = DecodeNextFrame(number);
1057 if (system_audio && !manual_system_audio) system_audio->AddFrameAudio(frame, number,
info.
fps);
1064 system_audio->AddFrameAudio(frame, output_frame_number,
info.
fps);
1070 manual_system_audio =
true;
1072 system_audio->Reset();
1076std::shared_ptr<Frame> ScreenCaptureReader::DecodeNextFrame(int64_t number)
1078 while (!close_requested) {
1079 const int read_result = av_read_frame(format_context, packet);
1080 if (read_result == AVERROR(EAGAIN)) {
1081 std::this_thread::sleep_for(std::chrono::milliseconds(5));
1084 if (read_result < 0) {
1085 const std::string message = frames_read > 0
1086 ?
"Capture input ended after " + std::to_string(frames_read) +
" frame(s): "
1087 :
"Capture input ended before a frame could be read: ";
1091 if (packet->stream_index != video_stream) {
1092 av_packet_unref(packet);
1097 const int64_t packet_timestamp = packet->pts != AV_NOPTS_VALUE ? packet->pts : packet->dts;
1098 const int send_result = avcodec_send_packet(codec_context, packet);
1099 av_packet_unref(packet);
1100 if (send_result < 0) {
1104 const int receive_result = avcodec_receive_frame(codec_context, source_frame);
1105 if (receive_result == AVERROR(EAGAIN)) {
1108 if (receive_result < 0) {
1109 throw InvalidFile(
"Unable to decode capture frame: " + std::string(
av_err2str(receive_result)), InputName());
1112 const AVStream* stream = format_context->streams[video_stream];
1113 int64_t frame_timestamp = source_frame->best_effort_timestamp;
1114 if (frame_timestamp == AV_NOPTS_VALUE) {
1115 frame_timestamp = source_frame->pts;
1117 if (frame_timestamp == AV_NOPTS_VALUE) {
1118 frame_timestamp = packet_timestamp;
1120 double capture_timestamp = std::numeric_limits<double>::quiet_NaN();
1121 if (frame_timestamp != AV_NOPTS_VALUE && stream) {
1122 capture_timestamp =
static_cast<double>(frame_timestamp) * av_q2d(stream->time_base);
1125 const int width = source_frame->width > 0 ? source_frame->width :
info.
width;
1126 const int height = source_frame->height > 0 ? source_frame->height :
info.
height;
1127 bool src_full_range = source_frame->color_range == AVCOL_RANGE_JPEG;
1129 static_cast<PixelFormat>(source_frame->format), src_full_range);
1131 sws_context = sws_getCachedContext(
1144 throw InvalidFile(
"Unable to create capture pixel conversion context.", InputName());
1147 const int* coefficients = sws_getCoefficients(SWS_CS_DEFAULT);
1148 sws_setColorspaceDetails(sws_context, coefficients, src_full_range ? 1 : 0,
1149 coefficients, 1, 0, 1 << 16, 1 << 16);
1151 const int bytes_per_pixel = 4;
1152 const size_t buffer_size =
static_cast<size_t>(width) * height * bytes_per_pixel;
1153 unsigned char* buffer =
static_cast<unsigned char*
>(aligned_malloc(buffer_size));
1155 throw OutOfMemory(
"Unable to allocate capture frame buffer.", InputName());
1158 av_image_fill_arrays(rgba_frame->data, rgba_frame->linesize, buffer, AV_PIX_FMT_RGBA, width, height, 1);
1159 const int scaled_lines = sws_scale(sws_context, source_frame->data, source_frame->linesize, 0, height, rgba_frame->data, rgba_frame->linesize);
1160 av_frame_unref(source_frame);
1162 if (scaled_lines <= 0) {
1167 int output_width = width;
1168 int output_height = height;
1169 unsigned char* output_buffer = buffer;
1170 if (option_enabled(settings.
options,
"crop_after_capture")) {
1171 const int crop_x = std::max(0, std::min(settings.
x, width - 1));
1172 const int crop_y = std::max(0, std::min(settings.
y, height - 1));
1173 output_width = std::max(1, std::min(settings.
width, width - crop_x));
1174 output_height = std::max(1, std::min(settings.
height, height - crop_y));
1175 const size_t output_buffer_size =
static_cast<size_t>(output_width) * output_height * bytes_per_pixel;
1176 output_buffer =
static_cast<unsigned char*
>(aligned_malloc(output_buffer_size));
1177 if (!output_buffer) {
1179 throw OutOfMemory(
"Unable to allocate cropped capture frame buffer.", InputName());
1181 for (
int row = 0; row < output_height; ++row) {
1182 const unsigned char* source_row = buffer
1183 + (
static_cast<size_t>(crop_y + row) * width + crop_x) * bytes_per_pixel;
1184 unsigned char* dest_row = output_buffer +
static_cast<size_t>(row) * output_width * bytes_per_pixel;
1185 std::copy(source_row, source_row +
static_cast<size_t>(output_width) * bytes_per_pixel, dest_row);
1190 auto frame = std::make_shared<Frame>(number, output_width, output_height,
"#000000");
1191 frame->capture_timestamp = capture_timestamp;
1192 frame->AddImage(output_width, output_height, bytes_per_pixel, QImage::Format_RGBA8888, output_buffer);
1197 throw ReaderClosed(
"The ScreenCaptureReader was closed.");
1202 close_requested =
true;
1210 close_requested =
true;
1211 if (backend_reader) {
1212 backend_reader->Close();
1217 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1219 system_audio->Close();
1222 av_packet_free(&packet);
1225 av_frame_free(&source_frame);
1228 av_frame_free(&rgba_frame);
1231 sws_freeContext(sws_context);
1232 sws_context =
nullptr;
1234 if (codec_context) {
1235 avcodec_free_context(&codec_context);
1237 if (format_context) {
1238 avformat_close_input(&format_context);
1246 if (backend_reader) {
1247 return backend_reader->GetStats();
1253 const double fps = settings.
fps.
den != 0 ?
static_cast<double>(settings.
fps.
num) /
static_cast<double>(settings.
fps.
den) : 0.0;
1254 stats.
duration = fps > 0.0 ?
static_cast<double>(frames_read) / fps : 0.0;
1266 root[
"type"] =
"ScreenCaptureReader";
1267 root[
"backend"] = settings.
backend;
1268 root[
"display"] = settings.
display;
1269 root[
"x"] = settings.
x;
1270 root[
"y"] = settings.
y;
1271 root[
"width"] = settings.
width;
1272 root[
"height"] = settings.
height;
1273 root[
"fps"][
"num"] = settings.
fps.
num;
1274 root[
"fps"][
"den"] = settings.
fps.
den;
1281 root[
"options"] = Json::Value(Json::objectValue);
1282 for (
const auto& option : settings.
options) {
1283 root[
"options"][option.first] = option.second;
1292 }
catch (
const std::exception&) {
1293 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
1299 if (!root[
"backend"].isNull())
1301 if (!root[
"display"].isNull())
1302 settings.
display = root[
"display"].asString();
1303 if (!root[
"x"].isNull())
1304 settings.
x = root[
"x"].asInt();
1305 if (!root[
"y"].isNull())
1306 settings.
y = root[
"y"].asInt();
1307 if (!root[
"width"].isNull())
1308 settings.
width = root[
"width"].asInt();
1309 if (!root[
"height"].isNull())
1310 settings.
height = root[
"height"].asInt();
1311 if (!root[
"fps"].isNull() && root[
"fps"].isObject()) {
1312 if (!root[
"fps"][
"num"].isNull())
1313 settings.
fps.
num = root[
"fps"][
"num"].asInt();
1314 if (!root[
"fps"][
"den"].isNull())
1315 settings.
fps.
den = root[
"fps"][
"den"].asInt();
1317 if (!root[
"include_cursor"].isNull())
1319 if (!root[
"show_region"].isNull())
1320 settings.
show_region = root[
"show_region"].asBool();
1321 if (!root[
"capture_audio"].isNull())
1323 if (!root[
"audio_device"].isNull())
1324 settings.
audio_device = root[
"audio_device"].asString();
1325 if (!root[
"audio_sample_rate"].isNull())
1327 if (!root[
"audio_channels"].isNull())
1329 if (!root[
"options"].isNull() && root[
"options"].isObject()) {
1331 for (
const auto& key : root[
"options"].getMemberNames()) {
1332 settings.
options[key] = root[
"options"][key].asString();
1340#if defined(__linux__) || defined(_WIN32)
1341 system_audio.reset();
1343 system_audio = std::make_unique<SystemAudioCapture>(settings);
Header file for all Exception classes.
AVPixelFormat NormalizeDeprecatedPixFmt(AVPixelFormat pix_fmt, bool &is_full_range)
#define av_err2str(errnum)
Header file for Frame class.
Header file for QtUtilities (compatibiity overlay)
Header file for live screen capture readers.
void AddFrameAudio(const std::shared_ptr< Frame > &, int64_t, const Fraction &)
SystemAudioCapture(const ScreenCaptureSettings &)
This class represents a fraction.
int num
Numerator for the fraction.
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
void Reduce()
Reduce this fraction (i.e. 640/480 = 4/3)
Fraction Reciprocal() const
Return the reciprocal as a Fraction.
int den
Denominator for the fraction.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Exception when an invalid # of audio channels are detected.
Exception when no valid codec is found for a file.
Exception for files that can not be found or opened.
Exception for invalid JSON.
Exception when invalid encoding options are used.
Exception when invalid sample rate is detected during encoding.
Exception when memory could not be allocated.
openshot::ReaderInfo info
Information about the current media file.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Exception when a reader is closed, and a frame is requested.
void SetJson(const std::string value) override
Load JSON string into this object.
bool IsOpen() override
Determine if reader is open or closed.
~ScreenCaptureReader() override
void RequestClose()
Signal FFmpeg capture reads to stop; the opening thread must call Close().
openshot::CaptureReaderStats GetStats() const
void Close() override
Close the reader (and any resources it was consuming)
static ScreenCaptureBackend DefaultBackend()
static bool IsBackendSupported(ScreenCaptureBackend backend)
ScreenCaptureReader(const ScreenCaptureSettings &settings)
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
void AddSystemAudio(std::shared_ptr< openshot::Frame > frame, int64_t output_frame_number)
Json::Value JsonValue() const override
Generate Json::Value for this object.
std::shared_ptr< openshot::Frame > GetFrame(int64_t number) override
std::string Json() const override
Generate JSON string of this object.
void Open() override
Open the reader (and start consuming resources, such as images or video files)
static bool IsSystemAudioSupported(ScreenCaptureBackend backend)
This namespace is the default namespace for all code in the openshot library.
void aligned_free(void *ptr)
@ SCREEN_CAPTURE_MAC_AVFOUNDATION
@ SCREEN_CAPTURE_WINDOWS_GDI
const Json::Value stringToJson(const std::string value)
This struct contains info about a media file, such as height, width, frames per second,...
int audio_bit_rate
The bit rate of the audio stream (in bytes)
int video_bit_rate
The bit rate of the video stream (in bytes)
bool has_single_image
Determines if this file only contains a single image.
float duration
Length of time (in seconds)
openshot::Fraction audio_timebase
The audio timebase determines how long each audio packet should be played.
int width
The width of the video (in pixesl)
int channels
The number of audio channels used in the audio stream.
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
openshot::Fraction display_ratio
The ratio of width to height of the video stream (i.e. 640x480 has a ratio of 4/3)
int height
The height of the video (in pixels)
int pixel_format
The pixel format (i.e. YUV420P, RGB24, etc...)
int64_t video_length
The number of frames in the video stream.
std::string acodec
The name of the audio codec used to encode / decode the video stream.
std::string vcodec
The name of the video codec used to encode / decode the video stream.
openshot::Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square)
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
bool has_video
Determines if this file has a video stream.
bool has_audio
Determines if this file has an audio stream.
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
int video_stream_index
The index of the video stream.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
int audio_stream_index
The index of the audio stream.
int64_t file_size
Size of file (in bytes)
std::map< std::string, std::string > options
ScreenCaptureBackend backend