OpenShot Library | libopenshot  1.0.0
ScreenCaptureReader.cpp
Go to the documentation of this file.
1 
9 // Copyright (c) 2008-2026 OpenShot Studios, LLC
10 //
11 // SPDX-License-Identifier: LGPL-3.0-or-later
12 
13 #include "ScreenCaptureReader.h"
14 
15 #include <algorithm>
16 #include <cctype>
17 #include <chrono>
18 #include <condition_variable>
19 #include <cstdlib>
20 #include <deque>
21 #include <limits>
22 #include <mutex>
23 #include <sstream>
24 #include <thread>
25 #include <vector>
26 
27 #if defined(__linux__)
28  #include <dlfcn.h>
29 #endif
30 
31 extern "C" {
32  #include <libavdevice/avdevice.h>
33  #include <libavutil/imgutils.h>
34 }
35 
36 #if defined(_WIN32)
37  // Instantiate the Core Audio COM GUIDs in this translation unit. Older
38  // MinGW-w64 uuid import libraries do not provide all WASAPI identifiers.
39  #include <initguid.h>
40  #include <audioclient.h>
41  #include <mmdeviceapi.h>
42 #endif
43 
44 #include "Exceptions.h"
45 #include "Frame.h"
46 #include "QtUtilities.h"
47 
48 using namespace openshot;
49 
50 #if defined(HAVE_WAYLAND_CAPTURE_PLUGIN) && defined(__linux__)
51 namespace
52 {
53  using WaylandBackendFactory = std::unique_ptr<ScreenCaptureReader::CaptureBackendReader> (*) (
55 
56  void* load_wayland_capture_backend(std::string& error_message)
57  {
58  Dl_info library_info {};
59  if (dladdr(reinterpret_cast<void*>(load_wayland_capture_backend), &library_info)
60  && library_info.dli_fname) {
61  std::string library_path(library_info.dli_fname);
62  const auto separator = library_path.find_last_of('/');
63  if (separator != std::string::npos) {
64  const std::string module_path = library_path.substr(0, separator + 1)
65  + "libopenshot-wayland-capture.so";
66  if (void* module = dlopen(module_path.c_str(), RTLD_NOW | RTLD_LOCAL)) {
67  return module;
68  }
69  const char* error = dlerror();
70  error_message = error ? error : "unable to load PipeWire support.";
71  return nullptr;
72  }
73  }
74  void* module = dlopen("libopenshot-wayland-capture.so", RTLD_NOW | RTLD_LOCAL);
75  if (!module) {
76  const char* error = dlerror();
77  error_message = error ? error : "unable to load PipeWire support.";
78  }
79  return module;
80  }
81 }
82 #endif
83 
84 #if defined(__linux__)
86 {
87 public:
88  explicit SystemAudioCapture(const ScreenCaptureSettings& new_settings)
89  : settings(new_settings) {}
90 
91  ~SystemAudioCapture() { Close(); }
92 
93  void Open()
94  {
95  if (format_context) return;
96  avdevice_register_all();
97  AVInputFormat* input_format = const_cast<AVInputFormat*>(av_find_input_format("pulse"));
98  if (!input_format) {
99  throw InvalidOptions("FFmpeg PulseAudio input is unavailable for system audio capture.");
100  }
101 
102  format_context = avformat_alloc_context();
103  if (!format_context) {
104  throw OutOfMemory("Unable to allocate system audio capture context.");
105  }
106  format_context->interrupt_callback.callback = InterruptCallback;
107  format_context->interrupt_callback.opaque = &close_requested;
108 
109  AVDictionary* options = nullptr;
110  av_dict_set(&options, "sample_rate", std::to_string(settings.audio_sample_rate).c_str(), 0);
111  av_dict_set(&options, "channels", std::to_string(settings.audio_channels).c_str(), 0);
112  const std::string device = settings.audio_device.empty() ? "@DEFAULT_MONITOR@" : settings.audio_device;
113  const int open_result = avformat_open_input(&format_context, device.c_str(), input_format, &options);
114  av_dict_free(&options);
115  if (open_result < 0) {
116  throw InvalidFile("Unable to open system audio capture input: " + std::string(av_err2str(open_result)), device);
117  }
118  if (avformat_find_stream_info(format_context, nullptr) < 0) {
119  throw InvalidFile("Unable to read system audio capture stream information.", device);
120  }
121  for (unsigned int index = 0; index < format_context->nb_streams; ++index) {
122  if (format_context->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
123  audio_stream = static_cast<int>(index);
124  break;
125  }
126  }
127  if (audio_stream < 0) {
128  throw InvalidFile("No audio stream found in system audio capture input.", device);
129  }
130  AVStream* stream = format_context->streams[audio_stream];
131  const AVCodec* codec = avcodec_find_decoder(stream->codecpar->codec_id);
132  codec_context = codec ? ffmpeg_get_codec_context(stream, codec) : nullptr;
133  if (!codec_context || avcodec_open2(codec_context, codec, nullptr) < 0) {
134  throw InvalidCodec("Unable to open system audio capture decoder.", device);
135  }
136  packet = av_packet_alloc();
137  decoded_frame = av_frame_alloc();
138  if (!packet || !decoded_frame) {
139  throw OutOfMemory("Unable to allocate system audio capture buffers.");
140  }
141  settings.audio_sample_rate = codec_context->sample_rate > 0
142  ? codec_context->sample_rate : settings.audio_sample_rate;
143  channels.assign(settings.audio_channels, {});
144  close_requested = false;
145  worker = std::thread(&SystemAudioCapture::CaptureLoop, this);
146  }
147 
148  void Close()
149  {
150  close_requested = true;
151  ready.notify_all();
152  if (worker.joinable()) {
153  worker.join();
154  }
155  if (decoded_frame) av_frame_free(&decoded_frame);
156  if (packet) av_packet_free(&packet);
157  if (codec_context) avcodec_free_context(&codec_context);
158  if (format_context) avformat_close_input(&format_context);
159  }
160 
161  void AddFrameAudio(const std::shared_ptr<Frame>& frame, int64_t number, const Fraction& fps)
162  {
163  int sample_count = 0;
164  for (int64_t frame_number = last_output_frame + 1; frame_number <= number; ++frame_number) {
165  sample_count += Frame::GetSamplesPerFrame(
166  frame_number, fps, settings.audio_sample_rate, settings.audio_channels);
167  }
168  last_output_frame = std::max(last_output_frame, number);
169  if (!frame || sample_count <= 0) return;
170 
171  std::unique_lock<std::mutex> lock(queue_mutex);
172  // The capture backend commonly delivers its first buffer later than the
173  // first video frame. Writing silence after the old 100 ms timeout moved
174  // every subsequently-delivered sample later on the recording timeline.
175  // Allow the first frame to establish the audio epoch before falling back
176  // to the short steady-state wait used for later frames.
177  const auto wait_time = timeline_started
178  ? std::chrono::milliseconds(100)
179  : std::chrono::milliseconds(3000);
180  ready.wait_for(lock, wait_time, [this, sample_count]() {
181  return close_requested || (!channels.empty() && static_cast<int>(channels[0].size()) >= sample_count);
182  });
183  if (!channels.empty() && static_cast<int>(channels[0].size()) >= sample_count) {
184  timeline_started = true;
185  }
186  frame->SampleRate(settings.audio_sample_rate);
187  frame->ChannelsLayout(settings.audio_channels == 1 ? LAYOUT_MONO : LAYOUT_STEREO);
188  for (int channel = 0; channel < settings.audio_channels; ++channel) {
189  std::vector<float> samples(sample_count, 0.0f);
190  if (channel < static_cast<int>(channels.size())) {
191  const int available = std::min(sample_count, static_cast<int>(channels[channel].size()));
192  for (int index = 0; index < available; ++index) {
193  samples[index] = channels[channel].front();
194  channels[channel].pop_front();
195  }
196  }
197  frame->AddAudio(true, channel, 0, samples.data(), sample_count, 1.0f);
198  }
199  }
200 
201  void Reset()
202  {
203  std::lock_guard<std::mutex> lock(queue_mutex);
204  for (auto& channel : channels) channel.clear();
205  last_output_frame = 0;
206  timeline_started = false;
207  }
208 
209  int SampleRate() const { return settings.audio_sample_rate; }
210  int Channels() const { return settings.audio_channels; }
211 
212 private:
213  static int InterruptCallback(void* opaque)
214  {
215  const auto* requested = static_cast<std::atomic<bool>*>(opaque);
216  return requested && requested->load() ? 1 : 0;
217  }
218 
219  float SampleAt(const AVFrame* frame, int channel, int sample) const
220  {
221  const AVSampleFormat format = static_cast<AVSampleFormat>(frame->format);
222  const bool planar = av_sample_fmt_is_planar(format) != 0;
223  const int source_channels = std::max(1,
224 #if HAVE_CH_LAYOUT
225  codec_context->ch_layout.nb_channels
226 #else
227  codec_context->channels
228 #endif
229  );
230  const int source_channel = std::min(channel, source_channels - 1);
231  const uint8_t* data = planar ? frame->extended_data[source_channel] : frame->extended_data[0];
232  const int offset = planar ? sample : sample * source_channels + source_channel;
233  switch (format) {
234  case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P:
235  return (static_cast<const uint8_t*>(static_cast<const void*>(data))[offset] - 128) / 128.0f;
236  case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P:
237  return static_cast<const int16_t*>(static_cast<const void*>(data))[offset] / 32768.0f;
238  case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P:
239  return static_cast<float>(static_cast<const int32_t*>(static_cast<const void*>(data))[offset] / 2147483648.0);
240  case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP:
241  return static_cast<const float*>(static_cast<const void*>(data))[offset];
242  case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBLP:
243  return static_cast<float>(static_cast<const double*>(static_cast<const void*>(data))[offset]);
244  default:
245  return 0.0f;
246  }
247  }
248 
249  void CaptureLoop()
250  {
251  while (!close_requested) {
252  const int read_result = av_read_frame(format_context, packet);
253  if (read_result == AVERROR(EAGAIN)) continue;
254  if (read_result < 0) break;
255  if (packet->stream_index != audio_stream) {
256  av_packet_unref(packet);
257  continue;
258  }
259  const int send_result = avcodec_send_packet(codec_context, packet);
260  av_packet_unref(packet);
261  if (send_result < 0) continue;
262  while (avcodec_receive_frame(codec_context, decoded_frame) == 0) {
263  std::lock_guard<std::mutex> lock(queue_mutex);
264  for (int channel = 0; channel < settings.audio_channels; ++channel) {
265  const size_t max_samples = static_cast<size_t>(settings.audio_sample_rate) * 10;
266  for (int sample = 0; sample < decoded_frame->nb_samples; ++sample) {
267  if (channels[channel].size() >= max_samples) channels[channel].pop_front();
268  channels[channel].push_back(SampleAt(decoded_frame, channel, sample));
269  }
270  }
271  av_frame_unref(decoded_frame);
272  ready.notify_all();
273  }
274  }
275  }
276 
277  ScreenCaptureSettings settings;
278  AVFormatContext* format_context = nullptr;
279  AVCodecContext* codec_context = nullptr;
280  AVPacket* packet = nullptr;
281  AVFrame* decoded_frame = nullptr;
282  int audio_stream = -1;
283  std::atomic<bool> close_requested { false };
284  std::thread worker;
285  std::mutex queue_mutex;
286  std::condition_variable ready;
287  std::vector<std::deque<float>> channels;
288  int64_t last_output_frame = 0;
289  bool timeline_started = false;
290 };
291 #elif defined(_WIN32)
293 {
294 public:
295  explicit SystemAudioCapture(const ScreenCaptureSettings& new_settings)
296  : settings(new_settings) {}
297 
298  ~SystemAudioCapture() { Close(); }
299 
300  void Open()
301  {
302  if (worker.joinable()) return;
303  close_requested = false;
304  worker = std::thread(&SystemAudioCapture::CaptureLoop, this);
305  std::unique_lock<std::mutex> lock(state_mutex);
306  state_ready.wait_for(lock, std::chrono::seconds(5), [this]() { return opened || failed; });
307  if (!opened) {
308  close_requested = true;
309  lock.unlock();
310  if (worker.joinable()) worker.join();
311  throw InvalidOptions(error.empty() ? "Unable to open WASAPI system audio loopback capture." : error);
312  }
313  }
314 
315  void Close()
316  {
317  close_requested = true;
318  ready.notify_all();
319  if (worker.joinable()) worker.join();
320  opened = false;
321  }
322 
323  void AddFrameAudio(const std::shared_ptr<Frame>& frame, int64_t number, const Fraction& fps)
324  {
325  int sample_count = 0;
326  for (int64_t frame_number = last_output_frame + 1; frame_number <= number; ++frame_number) {
327  sample_count += Frame::GetSamplesPerFrame(frame_number, fps, sample_rate, channel_count);
328  }
329  last_output_frame = std::max(last_output_frame, number);
330  if (!frame || sample_count <= 0) return;
331  std::unique_lock<std::mutex> lock(queue_mutex);
332  // WASAPI can take longer than one video-frame interval to make its first
333  // loopback packet available. Keep that startup latency out of the encoded
334  // media timeline by waiting for the first complete frame of audio.
335  const auto wait_time = timeline_started
336  ? std::chrono::milliseconds(100)
337  : std::chrono::milliseconds(3000);
338  ready.wait_for(lock, wait_time, [this, sample_count]() {
339  return close_requested || (!channels.empty() && static_cast<int>(channels[0].size()) >= sample_count);
340  });
341  if (!channels.empty() && static_cast<int>(channels[0].size()) >= sample_count) {
342  timeline_started = true;
343  }
344  frame->SampleRate(sample_rate);
345  frame->ChannelsLayout(channel_count == 1 ? LAYOUT_MONO : LAYOUT_STEREO);
346  for (int channel = 0; channel < channel_count; ++channel) {
347  std::vector<float> samples(sample_count, 0.0f);
348  const int available = std::min(sample_count, static_cast<int>(channels[channel].size()));
349  for (int index = 0; index < available; ++index) {
350  samples[index] = channels[channel].front();
351  channels[channel].pop_front();
352  }
353  frame->AddAudio(true, channel, 0, samples.data(), sample_count, 1.0f);
354  }
355  }
356 
357  void Reset()
358  {
359  std::lock_guard<std::mutex> lock(queue_mutex);
360  for (auto& channel : channels) channel.clear();
361  last_output_frame = 0;
362  timeline_started = false;
363  }
364 
365  int SampleRate() const { return sample_rate; }
366  int Channels() const { return channel_count; }
367 
368 private:
369  template <typename T> static void Release(T*& value)
370  {
371  if (value) { value->Release(); value = nullptr; }
372  }
373 
374  void Fail(const std::string& message)
375  {
376  std::lock_guard<std::mutex> lock(state_mutex);
377  error = message;
378  failed = true;
379  state_ready.notify_all();
380  }
381 
382  void CaptureLoop()
383  {
384  IMMDeviceEnumerator* enumerator = nullptr;
385  IMMDevice* device = nullptr;
386  IAudioClient* client = nullptr;
387  IAudioCaptureClient* capture = nullptr;
388  WAVEFORMATEX* format = nullptr;
389  const HRESULT com_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
390  const bool uninitialize_com = SUCCEEDED(com_result);
391  HRESULT result = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL,
392  IID_IMMDeviceEnumerator, reinterpret_cast<void**>(&enumerator));
393  if (SUCCEEDED(result)) {
394  result = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
395  }
396  if (SUCCEEDED(result)) {
397  result = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&client));
398  }
399  if (SUCCEEDED(result)) result = client->GetMixFormat(&format);
400  if (SUCCEEDED(result)) {
401  result = client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK,
402  0, 0, format, nullptr);
403  }
404  if (SUCCEEDED(result)) {
405  result = client->GetService(IID_IAudioCaptureClient, reinterpret_cast<void**>(&capture));
406  }
407  if (SUCCEEDED(result)) result = client->Start();
408  if (FAILED(result) || !format || !capture) {
409  Fail("Unable to initialize WASAPI system audio loopback capture (HRESULT " + std::to_string(result) + ").");
410  if (format) CoTaskMemFree(format);
411  Release(capture); Release(client); Release(device); Release(enumerator);
412  if (uninitialize_com) CoUninitialize();
413  return;
414  }
415 
416  sample_rate = static_cast<int>(format->nSamplesPerSec);
417  channel_count = std::max(1, std::min(2, static_cast<int>(format->nChannels)));
418  channels.assign(channel_count, {});
419  bool floating_point = format->wFormatTag == WAVE_FORMAT_IEEE_FLOAT;
420  if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= 22) {
421  const auto* extensible = reinterpret_cast<const WAVEFORMATEXTENSIBLE*>(format);
422  floating_point = extensible->SubFormat.Data1 == WAVE_FORMAT_IEEE_FLOAT;
423  }
424  {
425  std::lock_guard<std::mutex> lock(state_mutex);
426  opened = true;
427  state_ready.notify_all();
428  }
429 
430  while (!close_requested) {
431  UINT32 packet_frames = 0;
432  if (FAILED(capture->GetNextPacketSize(&packet_frames))) break;
433  if (packet_frames == 0) {
434  std::this_thread::sleep_for(std::chrono::milliseconds(2));
435  continue;
436  }
437  BYTE* data = nullptr;
438  UINT32 frames = 0;
439  DWORD flags = 0;
440  if (FAILED(capture->GetBuffer(&data, &frames, &flags, nullptr, nullptr))) break;
441  {
442  std::lock_guard<std::mutex> lock(queue_mutex);
443  for (UINT32 frame_index = 0; frame_index < frames; ++frame_index) {
444  for (int channel = 0; channel < channel_count; ++channel) {
445  float sample = 0.0f;
446  if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT) && data) {
447  const int offset = static_cast<int>(frame_index) * format->nChannels + channel;
448  if (floating_point && format->wBitsPerSample == 32) {
449  sample = reinterpret_cast<const float*>(data)[offset];
450  } else if (format->wBitsPerSample == 16) {
451  sample = reinterpret_cast<const int16_t*>(data)[offset] / 32768.0f;
452  } else if (format->wBitsPerSample == 32) {
453  sample = static_cast<float>(reinterpret_cast<const int32_t*>(data)[offset] / 2147483648.0);
454  }
455  }
456  const size_t max_samples = static_cast<size_t>(sample_rate) * 10;
457  if (channels[channel].size() >= max_samples) channels[channel].pop_front();
458  channels[channel].push_back(sample);
459  }
460  }
461  }
462  capture->ReleaseBuffer(frames);
463  ready.notify_all();
464  }
465 
466  client->Stop();
467  CoTaskMemFree(format);
468  Release(capture); Release(client); Release(device); Release(enumerator);
469  if (uninitialize_com) CoUninitialize();
470  }
471 
472  ScreenCaptureSettings settings;
473  std::atomic<bool> close_requested { false };
474  std::thread worker;
475  std::mutex queue_mutex;
476  std::condition_variable ready;
477  std::vector<std::deque<float>> channels;
478  int64_t last_output_frame = 0;
479  bool timeline_started = false;
480  int sample_rate = 48000;
481  int channel_count = 2;
482  std::mutex state_mutex;
483  std::condition_variable state_ready;
484  bool opened = false;
485  bool failed = false;
486  std::string error;
487 };
488 #else
490 {
491 public:
493  void Open() {}
494  void Close() {}
495  void AddFrameAudio(const std::shared_ptr<Frame>&, int64_t, const Fraction&) {}
496  void Reset() {}
497  int SampleRate() const { return 0; }
498  int Channels() const { return 0; }
499 };
500 #endif
501 
502 namespace
503 {
504  std::string fraction_to_string(const Fraction& value)
505  {
506  std::stringstream stream;
507  stream << value.num << "/" << value.den;
508  return stream.str();
509  }
510 
511  void set_option(AVDictionary** options, const std::string& key, const std::string& value)
512  {
513  av_dict_set(options, key.c_str(), value.c_str(), 0);
514  }
515 
516  bool option_enabled(const std::map<std::string, std::string>& options, const std::string& key)
517  {
518  const auto option = options.find(key);
519  if (option == options.end()) {
520  return false;
521  }
522  return option->second == "1" || option->second == "true" || option->second == "yes";
523  }
524 
525  int option_int(const std::map<std::string, std::string>& options, const std::string& key, int fallback)
526  {
527  const auto option = options.find(key);
528  if (option == options.end()) {
529  return fallback;
530  }
531  try {
532  return std::stoi(option->second);
533  } catch (...) {
534  return fallback;
535  }
536  }
537 
538  int capture_interrupt_callback(void* opaque)
539  {
540  const auto* reader = static_cast<std::atomic<bool>*>(opaque);
541  return reader && reader->load() ? 1 : 0;
542  }
543 
544  std::string avfoundation_video_name(const std::string& input_name)
545  {
546  const size_t separator = input_name.find(':');
547  return separator == std::string::npos ? input_name : input_name.substr(0, separator);
548  }
549 
550  std::string avfoundation_audio_name(const std::string& input_name)
551  {
552  const size_t separator = input_name.find(':');
553  return separator == std::string::npos ? "none" : input_name.substr(separator + 1);
554  }
555 
556  bool resolve_avfoundation_video_index(
557  AVInputFormat* input_format,
558  const std::string& video_name,
559  std::string& video_index)
560  {
561  const auto is_digit = [](unsigned char value) {
562  return std::isdigit(value) != 0;
563  };
564  if (video_name.empty()
565  || video_name == "default"
566  || video_name == "none"
567  || std::all_of(video_name.begin(), video_name.end(), is_digit)) {
568  return false;
569  }
570 
571  AVDeviceInfoList* device_list = nullptr;
572  const int result = avdevice_list_input_sources(input_format, nullptr, nullptr, &device_list);
573  if (result < 0 || !device_list) {
574  avdevice_free_list_devices(&device_list);
575  return false;
576  }
577 
578  bool found = false;
579  for (int index = 0; index < device_list->nb_devices; ++index) {
580  const AVDeviceInfo* device = device_list->devices[index];
581  if (!device || !device->device_name) {
582  continue;
583  }
584  const std::string name = device->device_name;
585  const std::string description = device->device_description
586  ? device->device_description
587  : device->device_name;
588  if (video_name == name || video_name == description) {
589  video_index = std::all_of(name.begin(), name.end(), is_digit)
590  ? name
591  : std::to_string(index);
592  found = true;
593  break;
594  }
595  }
596  avdevice_free_list_devices(&device_list);
597  return found;
598  }
599 }
600 
602  : settings(new_settings)
603  , backend_reader(nullptr)
604  , backend_module(nullptr)
605  , is_open(false)
606  , video_stream(-1)
607  , frames_read(0)
608  , dropped_packets(0)
609  , format_context(nullptr)
610  , codec_context(nullptr)
611  , source_frame(nullptr)
612  , rgba_frame(nullptr)
613  , packet(nullptr)
614  , sws_context(nullptr)
615  , close_requested(false)
616  , system_audio(nullptr)
617 {
618  if (settings.backend == SCREEN_CAPTURE_AUTO) {
619  settings.backend = DefaultBackend();
620  }
621  ValidateSettings();
622  PopulateInfo();
623 #if defined(__linux__) || defined(_WIN32)
624  if (settings.capture_audio) {
625  system_audio = std::make_unique<SystemAudioCapture>(settings);
626  }
627 #endif
628  if (UsesWaylandPortal()) {
629  #if defined(HAVE_WAYLAND_CAPTURE_PLUGIN) && defined(__linux__)
630  std::string module_error;
631  backend_module = load_wayland_capture_backend(module_error);
632  if (!backend_module) {
633  throw InvalidOptions("Wayland screen capture backend is unavailable: "
634  + module_error);
635  }
636  auto factory = reinterpret_cast<WaylandBackendFactory>(
637  dlsym(backend_module, "OpenShotCreateWaylandScreenCaptureReader"));
638  if (!factory) {
639  const char* error = dlerror();
640  dlclose(backend_module);
641  backend_module = nullptr;
642  throw InvalidOptions("Wayland screen capture backend is invalid: "
643  + std::string(error ? error : "factory function is missing."));
644  }
645  try {
646  backend_reader = factory(settings, info);
647  } catch (...) {
648  dlclose(backend_module);
649  backend_module = nullptr;
650  throw;
651  }
652  if (!backend_reader) {
653  dlclose(backend_module);
654  backend_module = nullptr;
655  throw InvalidOptions("Wayland screen capture backend is unavailable.");
656  }
657  #else
658  throw InvalidOptions("Wayland screen capture backend is unavailable in this build.");
659  #endif
660  }
661 }
662 
664 {
665  Close();
666 #if defined(__linux__)
667  backend_reader.reset();
668  if (backend_module) {
669  dlclose(backend_module);
670  backend_module = nullptr;
671  }
672 #endif
673 }
674 
676 {
677  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
678  return backend_reader ? backend_reader->IsOpen() : is_open;
679 }
680 
682 {
683 #if defined(__linux__)
684  if (backend == SCREEN_CAPTURE_X11 || backend == SCREEN_CAPTURE_AUTO) {
685  return true;
686  }
687 #if defined(HAVE_WAYLAND_CAPTURE_PLUGIN)
688  if (backend == SCREEN_CAPTURE_WAYLAND) {
689  return true;
690  }
691 #endif
692  return false;
693 #elif defined(_WIN32)
694  return backend == SCREEN_CAPTURE_WINDOWS_GDI || backend == SCREEN_CAPTURE_AUTO;
695 #elif defined(__APPLE__)
696  return backend == SCREEN_CAPTURE_MAC_AVFOUNDATION || backend == SCREEN_CAPTURE_AUTO;
697 #else
698  (void) backend;
699  return false;
700 #endif
701 }
702 
704 {
705 #if defined(__linux__)
706  if (backend == SCREEN_CAPTURE_AUTO) {
707  backend = DefaultBackend();
708  }
709  avdevice_register_all();
710  return (backend == SCREEN_CAPTURE_X11 || backend == SCREEN_CAPTURE_WAYLAND)
711  && av_find_input_format("pulse") != nullptr;
712 #elif defined(_WIN32)
713  return backend == SCREEN_CAPTURE_AUTO || backend == SCREEN_CAPTURE_WINDOWS_GDI;
714 #else
715  (void) backend;
716  return false;
717 #endif
718 }
719 
721 {
722 #if defined(__linux__)
723  const char* session = std::getenv("XDG_SESSION_TYPE");
724  if (session && std::string(session) == "wayland" && IsBackendSupported(SCREEN_CAPTURE_WAYLAND)) {
725  return SCREEN_CAPTURE_WAYLAND;
726  }
727  if (!session || std::string(session) == "x11") {
728  return SCREEN_CAPTURE_X11;
729  }
730 #elif defined(_WIN32)
732 #elif defined(__APPLE__)
734 #endif
735  return SCREEN_CAPTURE_AUTO;
736 }
737 
738 void ScreenCaptureReader::ValidateSettings() const
739 {
740  if (!IsBackendSupported(settings.backend)) {
741  throw InvalidOptions("Screen capture backend is not supported on this OS or session.");
742  }
743  if (!UsesFFmpegDevice() && !UsesWaylandPortal()) {
744  throw InvalidOptions("Screen capture backend is not implemented in this build.");
745  }
746  if (settings.width <= 0 || settings.height <= 0) {
747  throw InvalidOptions("Screen capture requires a positive width and height.");
748  }
749  if (settings.fps.num <= 0 || settings.fps.den <= 0) {
750  throw InvalidOptions("Screen capture requires a positive frame rate.");
751  }
752  if (settings.capture_audio && !IsSystemAudioSupported(settings.backend)) {
753  throw InvalidOptions("System audio capture is not supported by this screen capture backend.");
754  }
755  if (settings.audio_sample_rate < 8000) {
756  throw InvalidSampleRate("System audio capture requires a sample rate of at least 8000 Hz.");
757  }
758  if (settings.audio_channels < 1 || settings.audio_channels > 2) {
759  throw InvalidChannels("System audio capture requires one or two channels.");
760  }
761 }
762 
763 bool ScreenCaptureReader::UsesFFmpegDevice() const
764 {
765  return settings.backend == SCREEN_CAPTURE_X11
766  || settings.backend == SCREEN_CAPTURE_WINDOWS_GDI
768  || settings.options.count("input_format_name");
769 }
770 
771 bool ScreenCaptureReader::UsesWaylandPortal() const
772 {
773  return settings.backend == SCREEN_CAPTURE_WAYLAND;
774 }
775 
776 std::string ScreenCaptureReader::InputFormatName() const
777 {
778  const auto override_format = settings.options.find("input_format_name");
779  if (override_format != settings.options.end()) {
780  return override_format->second;
781  }
782  if (settings.backend == SCREEN_CAPTURE_X11) {
783  return "x11grab";
784  }
785  if (settings.backend == SCREEN_CAPTURE_WINDOWS_GDI) {
786  return "gdigrab";
787  }
788  if (settings.backend == SCREEN_CAPTURE_MAC_AVFOUNDATION) {
789  return "avfoundation";
790  }
791  return "";
792 }
793 
794 std::string ScreenCaptureReader::InputName() const
795 {
796  if (InputFormatName() == "v4l2") {
797  return settings.display.empty() ? "/dev/video0" : settings.display;
798  }
799  if (InputFormatName() == "dshow") {
800  return settings.display;
801  }
802  if (InputFormatName() == "gdigrab") {
803  return settings.display.empty() ? "desktop" : settings.display;
804  }
805  if (InputFormatName() == "avfoundation") {
806  return settings.display.empty() ? "Capture screen 0:none" : settings.display;
807  }
808 
809  std::string display = settings.display;
810  if (display.empty()) {
811  const char* env_display = std::getenv("DISPLAY");
812  display = env_display ? env_display : ":0.0";
813  }
814  if (InputFormatName() == "x11grab" && settings.options.count("window_id")) {
815  return display;
816  }
817 
818  std::stringstream input;
819  input << display << "+" << settings.x << "," << settings.y;
820  return input.str();
821 }
822 
823 void ScreenCaptureReader::PopulateInfo()
824 {
825  info.has_video = true;
826  info.has_audio = settings.capture_audio;
827  info.has_single_image = false;
828  info.duration = 60.0f * 60.0f;
829  info.file_size = 0;
830  info.width = settings.width;
831  info.height = settings.height;
832  info.pixel_format = AV_PIX_FMT_RGBA;
833  info.fps = settings.fps;
834  info.video_bit_rate = 0;
835  info.pixel_ratio = Fraction(1, 1);
836  info.display_ratio = Fraction(settings.width, settings.height);
838  info.vcodec = "rawvideo";
839  info.video_length = static_cast<int64_t>(info.duration * settings.fps.ToFloat());
841  info.video_timebase = settings.fps.Reciprocal();
842  info.interlaced_frame = false;
843  info.top_field_first = false;
844  info.acodec = settings.capture_audio ? "pcm_f32le" : "";
845  info.audio_bit_rate = 0;
846  info.sample_rate = settings.capture_audio ? settings.audio_sample_rate : 0;
847  info.channels = settings.capture_audio ? settings.audio_channels : 0;
850  info.audio_timebase = Fraction(1, 1);
851 }
852 
854 {
855  if (backend_reader) {
856  if (backend_reader->IsOpen()) return;
857  manual_system_audio = false;
858  if (system_audio) {
859  system_audio->Open();
860  info.sample_rate = system_audio->SampleRate();
861  info.channels = system_audio->Channels();
863  }
864  try {
865  backend_reader->Open();
866  } catch (...) {
867  if (system_audio) system_audio->Close();
868  throw;
869  }
870  return;
871  }
872  if (is_open) {
873  return;
874  }
875  manual_system_audio = false;
876  if (system_audio) {
877  system_audio->Open();
878  info.sample_rate = system_audio->SampleRate();
879  info.channels = system_audio->Channels();
881  }
882  close_requested = false;
883  try {
884  OpenDevice();
885  OpenDecoder();
886  } catch (...) {
887  if (system_audio) system_audio->Close();
888  throw;
889  }
890  is_open = true;
891 }
892 
893 void ScreenCaptureReader::OpenDevice()
894 {
895  avdevice_register_all();
896 
897  AVInputFormat* input_format = const_cast<AVInputFormat*>(av_find_input_format(InputFormatName().c_str()));
898  std::string input_name = InputName();
899  if (!input_format) {
900  throw InvalidOptions("FFmpeg input device is not available: " + InputFormatName(), input_name);
901  }
902 
903  format_context = avformat_alloc_context();
904  if (!format_context) {
905  throw OutOfMemory("Unable to allocate capture format context.", input_name);
906  }
907  format_context->interrupt_callback.callback = capture_interrupt_callback;
908  format_context->interrupt_callback.opaque = &close_requested;
909 
910  AVDictionary* options = nullptr;
911  const bool use_device_defaults = settings.options.count("use_device_defaults") > 0;
912  const bool post_capture_crop = option_enabled(settings.options, "crop_after_capture");
913  if (!use_device_defaults && !post_capture_crop) {
914  set_option(&options, "framerate", fraction_to_string(settings.fps));
915  set_option(&options, "video_size", std::to_string(settings.width) + "x" + std::to_string(settings.height));
916  } else if (post_capture_crop) {
917  const int source_width = option_int(settings.options, "crop_source_width", settings.width);
918  const int source_height = option_int(settings.options, "crop_source_height", settings.height);
919  set_option(&options, "framerate", fraction_to_string(settings.fps));
920  set_option(&options, "video_size", std::to_string(source_width) + "x" + std::to_string(source_height));
921  }
922  if (InputFormatName() == "x11grab") {
923  set_option(&options, "draw_mouse", settings.include_cursor ? "1" : "0");
924  set_option(&options, "show_region", settings.show_region ? "1" : "0");
925  } else if (InputFormatName() == "gdigrab") {
926  set_option(&options, "draw_mouse", settings.include_cursor ? "1" : "0");
927  set_option(&options, "show_region", settings.show_region ? "1" : "0");
928  set_option(&options, "offset_x", std::to_string(settings.x));
929  set_option(&options, "offset_y", std::to_string(settings.y));
930  } else if (InputFormatName() == "avfoundation") {
931  set_option(&options, "capture_cursor", settings.include_cursor ? "1" : "0");
932  std::string video_index;
933  if (resolve_avfoundation_video_index(input_format, avfoundation_video_name(input_name), video_index)) {
934  set_option(&options, "video_device_index", video_index);
935  input_name = ":" + avfoundation_audio_name(input_name);
936  }
937  }
938  for (const auto& option : settings.options) {
939  if (option.first == "input_format_name"
940  || option.first == "use_device_defaults"
941  || option.first == "crop_after_capture"
942  || option.first == "crop_source_width"
943  || option.first == "crop_source_height") {
944  continue;
945  }
946  set_option(&options, option.first, option.second);
947  }
948 
949  const int result = avformat_open_input(&format_context, input_name.c_str(), input_format, &options);
950  av_dict_free(&options);
951  if (result < 0) {
952  throw InvalidFile("Unable to open screen capture input: " + std::string(av_err2str(result)), input_name);
953  }
954 }
955 
956 void ScreenCaptureReader::OpenDecoder()
957 {
958  if (avformat_find_stream_info(format_context, nullptr) < 0) {
959  throw InvalidFile("Unable to read capture stream information.", InputName());
960  }
961 
962  for (unsigned int index = 0; index < format_context->nb_streams; ++index) {
963  if (format_context->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
964  video_stream = static_cast<int>(index);
965  break;
966  }
967  }
968  if (video_stream < 0) {
969  throw InvalidFile("No video stream found in capture input.", InputName());
970  }
971 
972  AVStream* stream = format_context->streams[video_stream];
973  const AVCodec* codec = avcodec_find_decoder(stream->codecpar->codec_id);
974  if (!codec) {
975  throw InvalidCodec("No decoder available for capture stream.", InputName());
976  }
977 
978  codec_context = ffmpeg_get_codec_context(stream, codec);
979  if (!codec_context || avcodec_open2(codec_context, codec, nullptr) < 0) {
980  throw InvalidCodec("Unable to open capture stream decoder.", InputName());
981  }
982 
983  info.width = codec_context->width > 0 ? codec_context->width : settings.width;
984  info.height = codec_context->height > 0 ? codec_context->height : settings.height;
985  if (option_enabled(settings.options, "crop_after_capture")) {
986  info.width = settings.width;
987  info.height = settings.height;
988  }
989  info.video_stream_index = video_stream;
990  info.pixel_format = codec_context->pix_fmt;
993  if (codec_context->framerate.num > 0 && codec_context->framerate.den > 0) {
994  info.fps = Fraction(codec_context->framerate.num, codec_context->framerate.den);
996  } else if (stream->avg_frame_rate.num > 0 && stream->avg_frame_rate.den > 0) {
997  info.fps = Fraction(stream->avg_frame_rate.num, stream->avg_frame_rate.den);
999  } else if (stream->r_frame_rate.num > 0 && stream->r_frame_rate.den > 0) {
1000  info.fps = Fraction(stream->r_frame_rate.num, stream->r_frame_rate.den);
1002  }
1003 
1004  source_frame = av_frame_alloc();
1005  rgba_frame = av_frame_alloc();
1006  packet = av_packet_alloc();
1007  if (!source_frame || !rgba_frame || !packet) {
1008  throw OutOfMemory("Unable to allocate capture decoder frames.", InputName());
1009  }
1010 }
1011 
1012 std::shared_ptr<Frame> ScreenCaptureReader::GetFrame(int64_t number)
1013 {
1014  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
1015  if (backend_reader) {
1016  if (!backend_reader->IsOpen()) {
1017  throw ReaderClosed("The ScreenCaptureReader is closed. Call Open() before GetFrame().");
1018  }
1019  auto frame = backend_reader->GetFrame(number);
1020  if (system_audio && !manual_system_audio) system_audio->AddFrameAudio(frame, number, info.fps);
1021  return frame;
1022  }
1023  if (!is_open) {
1024  throw ReaderClosed("The ScreenCaptureReader is closed. Call Open() before GetFrame().");
1025  }
1026 
1027  auto frame = DecodeNextFrame(number);
1028  if (system_audio && !manual_system_audio) system_audio->AddFrameAudio(frame, number, info.fps);
1029  return frame;
1030 }
1031 
1032 void ScreenCaptureReader::AddSystemAudio(std::shared_ptr<Frame> frame, int64_t output_frame_number)
1033 {
1034  if (system_audio) {
1035  system_audio->AddFrameAudio(frame, output_frame_number, info.fps);
1036  }
1037 }
1038 
1040 {
1041  manual_system_audio = true;
1042  if (system_audio) {
1043  system_audio->Reset();
1044  }
1045 }
1046 
1047 std::shared_ptr<Frame> ScreenCaptureReader::DecodeNextFrame(int64_t number)
1048 {
1049  while (!close_requested) {
1050  const int read_result = av_read_frame(format_context, packet);
1051  if (read_result == AVERROR(EAGAIN)) {
1052  std::this_thread::sleep_for(std::chrono::milliseconds(5));
1053  continue;
1054  }
1055  if (read_result < 0) {
1056  const std::string message = frames_read > 0
1057  ? "Capture input ended after " + std::to_string(frames_read) + " frame(s): "
1058  : "Capture input ended before a frame could be read: ";
1059  throw InvalidFile(message + std::string(av_err2str(read_result)), InputName());
1060  }
1061 
1062  if (packet->stream_index != video_stream) {
1063  av_packet_unref(packet);
1064  dropped_packets++;
1065  continue;
1066  }
1067 
1068  const int64_t packet_timestamp = packet->pts != AV_NOPTS_VALUE ? packet->pts : packet->dts;
1069  const int send_result = avcodec_send_packet(codec_context, packet);
1070  av_packet_unref(packet);
1071  if (send_result < 0) {
1072  continue;
1073  }
1074 
1075  const int receive_result = avcodec_receive_frame(codec_context, source_frame);
1076  if (receive_result == AVERROR(EAGAIN)) {
1077  continue;
1078  }
1079  if (receive_result < 0) {
1080  throw InvalidFile("Unable to decode capture frame: " + std::string(av_err2str(receive_result)), InputName());
1081  }
1082 
1083  const AVStream* stream = format_context->streams[video_stream];
1084  int64_t frame_timestamp = source_frame->best_effort_timestamp;
1085  if (frame_timestamp == AV_NOPTS_VALUE) {
1086  frame_timestamp = source_frame->pts;
1087  }
1088  if (frame_timestamp == AV_NOPTS_VALUE) {
1089  frame_timestamp = packet_timestamp;
1090  }
1091  double capture_timestamp = std::numeric_limits<double>::quiet_NaN();
1092  if (frame_timestamp != AV_NOPTS_VALUE && stream) {
1093  capture_timestamp = static_cast<double>(frame_timestamp) * av_q2d(stream->time_base);
1094  }
1095 
1096  const int width = source_frame->width > 0 ? source_frame->width : info.width;
1097  const int height = source_frame->height > 0 ? source_frame->height : info.height;
1098  const PixelFormat src_fmt = static_cast<PixelFormat>(source_frame->format);
1099 
1100  sws_context = sws_getCachedContext(
1101  sws_context,
1102  width,
1103  height,
1104  src_fmt,
1105  width,
1106  height,
1107  AV_PIX_FMT_RGBA,
1108  SWS_BILINEAR,
1109  nullptr,
1110  nullptr,
1111  nullptr);
1112  if (!sws_context) {
1113  throw InvalidFile("Unable to create capture pixel conversion context.", InputName());
1114  }
1115 
1116  const int bytes_per_pixel = 4;
1117  const size_t buffer_size = static_cast<size_t>(width) * height * bytes_per_pixel;
1118  unsigned char* buffer = static_cast<unsigned char*>(aligned_malloc(buffer_size));
1119  if (!buffer) {
1120  throw OutOfMemory("Unable to allocate capture frame buffer.", InputName());
1121  }
1122 
1123  av_image_fill_arrays(rgba_frame->data, rgba_frame->linesize, buffer, AV_PIX_FMT_RGBA, width, height, 1);
1124  const int scaled_lines = sws_scale(sws_context, source_frame->data, source_frame->linesize, 0, height, rgba_frame->data, rgba_frame->linesize);
1125  av_frame_unref(source_frame);
1126 
1127  if (scaled_lines <= 0) {
1128  openshot::aligned_free(buffer);
1129  continue;
1130  }
1131 
1132  int output_width = width;
1133  int output_height = height;
1134  unsigned char* output_buffer = buffer;
1135  if (option_enabled(settings.options, "crop_after_capture")) {
1136  const int crop_x = std::max(0, std::min(settings.x, width - 1));
1137  const int crop_y = std::max(0, std::min(settings.y, height - 1));
1138  output_width = std::max(1, std::min(settings.width, width - crop_x));
1139  output_height = std::max(1, std::min(settings.height, height - crop_y));
1140  const size_t output_buffer_size = static_cast<size_t>(output_width) * output_height * bytes_per_pixel;
1141  output_buffer = static_cast<unsigned char*>(aligned_malloc(output_buffer_size));
1142  if (!output_buffer) {
1143  openshot::aligned_free(buffer);
1144  throw OutOfMemory("Unable to allocate cropped capture frame buffer.", InputName());
1145  }
1146  for (int row = 0; row < output_height; ++row) {
1147  const unsigned char* source_row = buffer
1148  + (static_cast<size_t>(crop_y + row) * width + crop_x) * bytes_per_pixel;
1149  unsigned char* dest_row = output_buffer + static_cast<size_t>(row) * output_width * bytes_per_pixel;
1150  std::copy(source_row, source_row + static_cast<size_t>(output_width) * bytes_per_pixel, dest_row);
1151  }
1152  openshot::aligned_free(buffer);
1153  }
1154 
1155  auto frame = std::make_shared<Frame>(number, output_width, output_height, "#000000");
1156  frame->capture_timestamp = capture_timestamp;
1157  frame->AddImage(output_width, output_height, bytes_per_pixel, QImage::Format_RGBA8888, output_buffer);
1158  frames_read++;
1159  return frame;
1160  }
1161 
1162  throw ReaderClosed("The ScreenCaptureReader was closed.");
1163 }
1164 
1166 {
1167  // Signal blocking native reads before waiting for GetFrame(). The FFmpeg
1168  // interrupt callback observes close_requested, while backend readers use
1169  // Close() to wake their own blocking waits.
1170  close_requested = true;
1171  if (backend_reader) {
1172  backend_reader->Close();
1173  }
1174 
1175  // GetFrame() owns all decoder and system-audio use under this mutex. Do not
1176  // release those resources until an interrupted read has completely exited.
1177  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
1178  if (system_audio) {
1179  system_audio->Close();
1180  }
1181  if (packet) {
1182  av_packet_free(&packet);
1183  }
1184  if (source_frame) {
1185  av_frame_free(&source_frame);
1186  }
1187  if (rgba_frame) {
1188  av_frame_free(&rgba_frame);
1189  }
1190  if (sws_context) {
1191  sws_freeContext(sws_context);
1192  sws_context = nullptr;
1193  }
1194  if (codec_context) {
1195  avcodec_free_context(&codec_context);
1196  }
1197  if (format_context) {
1198  avformat_close_input(&format_context);
1199  }
1200  is_open = false;
1201  video_stream = -1;
1202 }
1203 
1205 {
1206  if (backend_reader) {
1207  return backend_reader->GetStats();
1208  }
1209  CaptureReaderStats stats;
1210  stats.is_open = is_open;
1211  stats.frames_read = frames_read;
1212  stats.dropped_packets = dropped_packets;
1213  const double fps = settings.fps.den != 0 ? static_cast<double>(settings.fps.num) / static_cast<double>(settings.fps.den) : 0.0;
1214  stats.duration = fps > 0.0 ? static_cast<double>(frames_read) / fps : 0.0;
1215  return stats;
1216 }
1217 
1218 std::string ScreenCaptureReader::Json() const
1219 {
1220  return JsonValue().toStyledString();
1221 }
1222 
1224 {
1225  Json::Value root = ReaderBase::JsonValue();
1226  root["type"] = "ScreenCaptureReader";
1227  root["backend"] = settings.backend;
1228  root["display"] = settings.display;
1229  root["x"] = settings.x;
1230  root["y"] = settings.y;
1231  root["width"] = settings.width;
1232  root["height"] = settings.height;
1233  root["fps"]["num"] = settings.fps.num;
1234  root["fps"]["den"] = settings.fps.den;
1235  root["include_cursor"] = settings.include_cursor;
1236  root["show_region"] = settings.show_region;
1237  root["capture_audio"] = settings.capture_audio;
1238  root["audio_device"] = settings.audio_device;
1239  root["audio_sample_rate"] = settings.audio_sample_rate;
1240  root["audio_channels"] = settings.audio_channels;
1241  root["options"] = Json::Value(Json::objectValue);
1242  for (const auto& option : settings.options) {
1243  root["options"][option.first] = option.second;
1244  }
1245  return root;
1246 }
1247 
1248 void ScreenCaptureReader::SetJson(const std::string value)
1249 {
1250  try {
1252  } catch (const std::exception&) {
1253  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
1254  }
1255 }
1256 
1257 void ScreenCaptureReader::SetJsonValue(const Json::Value root)
1258 {
1259  if (!root["backend"].isNull())
1260  settings.backend = static_cast<ScreenCaptureBackend>(root["backend"].asInt());
1261  if (!root["display"].isNull())
1262  settings.display = root["display"].asString();
1263  if (!root["x"].isNull())
1264  settings.x = root["x"].asInt();
1265  if (!root["y"].isNull())
1266  settings.y = root["y"].asInt();
1267  if (!root["width"].isNull())
1268  settings.width = root["width"].asInt();
1269  if (!root["height"].isNull())
1270  settings.height = root["height"].asInt();
1271  if (!root["fps"].isNull() && root["fps"].isObject()) {
1272  if (!root["fps"]["num"].isNull())
1273  settings.fps.num = root["fps"]["num"].asInt();
1274  if (!root["fps"]["den"].isNull())
1275  settings.fps.den = root["fps"]["den"].asInt();
1276  }
1277  if (!root["include_cursor"].isNull())
1278  settings.include_cursor = root["include_cursor"].asBool();
1279  if (!root["show_region"].isNull())
1280  settings.show_region = root["show_region"].asBool();
1281  if (!root["capture_audio"].isNull())
1282  settings.capture_audio = root["capture_audio"].asBool();
1283  if (!root["audio_device"].isNull())
1284  settings.audio_device = root["audio_device"].asString();
1285  if (!root["audio_sample_rate"].isNull())
1286  settings.audio_sample_rate = root["audio_sample_rate"].asInt();
1287  if (!root["audio_channels"].isNull())
1288  settings.audio_channels = root["audio_channels"].asInt();
1289  if (!root["options"].isNull() && root["options"].isObject()) {
1290  settings.options.clear();
1291  for (const auto& key : root["options"].getMemberNames()) {
1292  settings.options[key] = root["options"][key].asString();
1293  }
1294  }
1295  if (settings.backend == SCREEN_CAPTURE_AUTO) {
1296  settings.backend = DefaultBackend();
1297  }
1298  ValidateSettings();
1299  PopulateInfo();
1300 #if defined(__linux__) || defined(_WIN32)
1301  system_audio.reset();
1302  if (settings.capture_audio) {
1303  system_audio = std::make_unique<SystemAudioCapture>(settings);
1304  }
1305 #endif
1306 }
Header file for all Exception classes.
#define PixelFormat
#define HAVE_CH_LAYOUT
#define av_err2str(errnum)
if(!codec) codec
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 &)
This class represents a fraction.
Definition: Fraction.h:30
int num
Numerator for the fraction.
Definition: Fraction.h:32
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:35
void Reduce()
Reduce this fraction (i.e. 640/480 = 4/3)
Definition: Fraction.cpp:65
Fraction Reciprocal() const
Return the reciprocal as a Fraction.
Definition: Fraction.cpp:78
int den
Denominator for the fraction.
Definition: Fraction.h:33
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Definition: Frame.cpp:486
Exception when an invalid # of audio channels are detected.
Definition: Exceptions.h:164
Exception when no valid codec is found for a file.
Definition: Exceptions.h:179
Exception for files that can not be found or opened.
Definition: Exceptions.h:194
Exception for invalid JSON.
Definition: Exceptions.h:224
Exception when invalid encoding options are used.
Definition: Exceptions.h:239
Exception when invalid sample rate is detected during encoding.
Definition: Exceptions.h:254
Exception when memory could not be allocated.
Definition: Exceptions.h:355
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:91
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:110
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Definition: ReaderBase.h:79
Exception when a reader is closed, and a frame is requested.
Definition: Exceptions.h:370
void SetJson(const std::string value) override
Load JSON string into this object.
bool IsOpen() override
Determine if reader is open or closed.
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.
Definition: AnimatedCurve.h:24
void aligned_free(void *ptr)
Definition: QtUtilities.h:32
@ SCREEN_CAPTURE_MAC_AVFOUNDATION
@ SCREEN_CAPTURE_WINDOWS_GDI
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
This struct contains info about a media file, such as height, width, frames per second,...
Definition: ReaderBase.h:39
int audio_bit_rate
The bit rate of the audio stream (in bytes)
Definition: ReaderBase.h:59
int video_bit_rate
The bit rate of the video stream (in bytes)
Definition: ReaderBase.h:49
bool has_single_image
Determines if this file only contains a single image.
Definition: ReaderBase.h:42
float duration
Length of time (in seconds)
Definition: ReaderBase.h:43
openshot::Fraction audio_timebase
The audio timebase determines how long each audio packet should be played.
Definition: ReaderBase.h:64
int width
The width of the video (in pixesl)
Definition: ReaderBase.h:46
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:61
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:48
openshot::Fraction display_ratio
The ratio of width to height of the video stream (i.e. 640x480 has a ratio of 4/3)
Definition: ReaderBase.h:51
int height
The height of the video (in pixels)
Definition: ReaderBase.h:45
int pixel_format
The pixel format (i.e. YUV420P, RGB24, etc...)
Definition: ReaderBase.h:47
int64_t video_length
The number of frames in the video stream.
Definition: ReaderBase.h:53
std::string acodec
The name of the audio codec used to encode / decode the video stream.
Definition: ReaderBase.h:58
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: ReaderBase.h:52
openshot::Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square)
Definition: ReaderBase.h:50
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
Definition: ReaderBase.h:62
bool has_video
Determines if this file has a video stream.
Definition: ReaderBase.h:40
bool has_audio
Determines if this file has an audio stream.
Definition: ReaderBase.h:41
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
Definition: ReaderBase.h:55
int video_stream_index
The index of the video stream.
Definition: ReaderBase.h:54
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:60
int audio_stream_index
The index of the audio stream.
Definition: ReaderBase.h:63
int64_t file_size
Size of file (in bytes)
Definition: ReaderBase.h:44
std::map< std::string, std::string > options