32#include <libavformat/avio.h>
39#include <sys/syscall.h>
43#include <libavutil/display.h>
60#if (defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS))
73 if (videoManagedByClient()) {
84VideoInput::startLoop()
86 if (videoManagedByClient()) {
95VideoInput::switchDevice()
97 if (switchPending_.exchange(
false)) {
99 if (decOpts_.
input.empty()) {
112 if (videoManagedByClient()) {
113 return static_cast<int>(decOpts_.
width);
115 return decoder_ ? decoder_->getWidth() : 0;
121 if (videoManagedByClient()) {
122 return static_cast<int>(decOpts_.
height);
124 return decoder_ ? decoder_->getHeight() : 0;
130 if (!videoManagedByClient()) {
131 return decoder_->getPixelFormat();
137VideoInput::setRotation(
int angle)
159 JAMI_DBG(
"VideoInput ready to capture");
168 if (paused_ || !decoder_->emitFrame(
false)) {
169 std::this_thread::sleep_for(std::chrono::milliseconds(20));
176 if (
not captureFrame()) {
186 decoder_->setSeekTime(
time);
199VideoInput::captureFrame()
205 switch (decoder_->decode()) {
208 return static_cast<bool>(decoder_);
210 JAMI_ERR() <<
"Failed to decode frame";
220 decoder_->flushBuffers();
230 auto decoder = std::make_unique<MediaDecoder>(
demuxer, index, [
this](std::shared_ptr<MediaFrame>&&
frame) {
233 decoder->setInterruptCallback([](
void* data) ->
int {
return not static_cast<VideoInput*
>(data)->isCapturing(); },
243 sink_->setFrameSize(decoder_->getWidth(), decoder_->getHeight());
247 decOpts_.
width = ((decoder_->getWidth() >> 3) << 3);
248 decOpts_.
height = ((decoder_->getHeight() >> 3) << 3);
254 JAMI_WARN(
"Unable to determine pixel format, using default");
258 if (onSuccessfulSetup_)
260 foundDecOpts(decOpts_);
261 futureDecOpts_ = foundDecOpts_.get_future().share();
267 recorderCallback_ =
cb;
269 decoder_->setContextCallback([
this]() {
270 if (recorderCallback_)
276VideoInput::createDecoder()
280 switchPending_ =
false;
282 if (decOpts_.
input.empty()) {
283 foundDecOpts(decOpts_);
287 auto decoder = std::make_unique<MediaDecoder>([
this](
const std::shared_ptr<MediaFrame>&
frame)
mutable {
294 decoder->setInterruptCallback([](
void* data) ->
int {
return not static_cast<VideoInput*
>(data)->isCapturing(); },
298 if ((decOpts_.
format ==
"x11grab" || decOpts_.
format ==
"dxgigrab" || decOpts_.
format ==
"pipewiregrab")
303 while (!ready && !isStopped_) {
308 JAMI_ERR(
"Unable to open input \"%s\" with status %i", decOpts_.
input.c_str(),
ret);
309 foundDecOpts(decOpts_);
317 std::this_thread::sleep_for(std::chrono::milliseconds(10));
328 if (
decoder->setupVideo() < 0) {
329 JAMI_ERR(
"decoder IO startup failed");
330 foundDecOpts(decOpts_);
347 JAMI_WARN(
"Unable to determine pixel format, using default");
351 JAMI_DBG(
"created decoder with video params : size=%dX%d, fps=%lf pix=%s",
356 if (onSuccessfulSetup_)
361 foundDecOpts(decOpts_);
364 sink_->setFrameSize(decoder_->getWidth(), decoder_->getHeight());
366 decoder_->setContextCallback([
this]() {
367 if (recorderCallback_)
373VideoInput::deleteDecoder()
382VideoInput::clearOptions()
385 emulateRate_ =
false;
391 if (videoManagedByClient()) {
398VideoInput::initCamera(
const std::string& device)
401 decOpts_ =
dm->getDeviceParams(device);
407static constexpr unsigned
410 return (
i >>
n) <<
n;
413#if !defined(WIN32) && !defined(__APPLE__)
415VideoInput::initLinuxGrab(
const std::string& display)
427 size_t space = display.find(
' ');
432 if (
winIdPos != std::string::npos) {
438 std::string
fpsStr =
"fps:";
439 if (display.find(
fpsStr) != std::string::npos) {
441 int fps = std::stoi(display.substr(
fpsPos));
445 if (display.find(
"pipewire") != std::string::npos) {
446 std::string
pidStr =
"pid:";
447 std::string
fdStr =
"fd:";
455 int fd = std::stoi(display.substr(
fdPos));
457#ifdef SYS_pidfd_getfd
462 JAMI_ERROR(
"Unable to duplicate PipeWire fd: call to pidfd_open failed (errno = {})",
errno);
467 JAMI_ERROR(
"Unable to duplicate PipeWire fd: call to pidfd_getfd failed (errno = {})",
errno);
471 JAMI_ERROR(
"Unable to duplicate PipeWire fd: pidfd_getfd syscall not available");
477 }
else if (
space != std::string::npos) {
495 emulateRate_ =
false;
503VideoInput::initAVFoundation(
const std::string& display)
509 size_t space = display.find(
' ');
512 decOpts_.
format =
"avfoundation";
514 decOpts_.
name =
"Capture screen 0";
515 decOpts_.
input =
"Capture screen 0";
518 if (
space != std::string::npos) {
519 std::istringstream
iss(display.substr(
space + 1));
535VideoInput::initWindowsGrab(
const std::string& display)
545 size_t space = display.find(
' ');
558 p.input = display.substr(1);
559 p.name = display.substr(1);
561 if (
space != std::string::npos) {
570 size_t plus = display.find(
'+');
582 std::string
fpsStr =
"fps:";
583 if (display.find(
fpsStr) != std::string::npos) {
585 int fps = std::stoi(display.substr(
fpsPos));
590 auto dec = std::make_unique<MediaDecoder>();
591 if (
dec->openInput(p) < 0 ||
dec->setupVideo() < 0)
596 decOpts_.
width =
dec->getStream().width;
597 decOpts_.
height =
dec->getStream().height;
604VideoInput::initFile(
const std::string& path)
610 size_t dot = path.find_last_of(
'.');
611 std::string
ext =
dot == std::string::npos ?
"" : path.substr(
dot + 1);
615 JAMI_ERR(
"file '%s' unavailable\n", path.c_str());
625 auto dec = std::make_unique<MediaDecoder>();
626 if (
dec->openInput(p) < 0 ||
dec->setupVideo() < 0) {
632 decOpts_.
input = path;
633 decOpts_.
name = path;
637 if (
ext ==
"jpeg" ||
ext ==
"jpg" ||
ext ==
"png") {
638 decOpts_.
format =
"image2";
641 JAMI_WARN(
"Guessing file type for %s", path.c_str());
651 switchInput(resource_);
654std::shared_future<DeviceParams>
655VideoInput::switchInput(
const std::string&
resource)
659 if (switchPending_.exchange(
true)) {
660 JAMI_ERR(
"Video switch already requested");
665 decOptsFound_ =
false;
667 std::promise<DeviceParams> p;
668 foundDecOpts_.swap(p);
671 if (resource_.empty()) {
673 futureDecOpts_ = foundDecOpts_.get_future();
675 return futureDecOpts_;
681 const auto pos = resource_.find(
sep);
682 if (
pos == std::string::npos)
685 const auto prefix = resource_.substr(0,
pos);
686 if ((
pos +
sep.size()) >= resource_.size())
695 ready = initCamera(
suffix);
703 ready = initLinuxGrab(
suffix);
711 foundDecOpts(decOpts_);
713 futureDecOpts_ = foundDecOpts_.get_future().share();
715 return futureDecOpts_;
721 if (!videoManagedByClient()) {
723 return decoder_->getStream(
"v:local");
725 auto opts = futureDecOpts_.get();
726 rational<int> fr(
static_cast<int>(
opts.framerate.numerator()),
static_cast<int>(
opts.framerate.denominator()));
730 static_cast<int>(
opts.width),
731 static_cast<int>(
opts.height),
739 if (
not decOptsFound_) {
740 decOptsFound_ =
true;
741 foundDecOpts_.set_value(
params);
756 sink_->setFrameSize(width, height);
static LIBJAMI_TEST_EXPORT Manager & instance()
bool attach(Observer< std::shared_ptr< MediaFrame > > *o)
bool detach(Observer< std::shared_ptr< MediaFrame > > *o)
bool isStopping() const noexcept
bool isRunning() const noexcept
Naive implementation of the boost::rational interface, described here: https://www....
void av_buffer_unref(AVBufferRef **buf)
#define JAMI_ERROR(formatstr,...)
#define JAMI_LOG(formatstr,...)
static constexpr const char DEVICE_DESKTOP[]
static constexpr unsigned default_grab_width
static constexpr unsigned default_grab_height
static constexpr unsigned round2pow(unsigned i, unsigned n)
void emitSignal(Args... args)
std::vector< unsigned > split_string_to_unsigned(std::string_view str, char delim)
DeviceParams Parameters used by MediaDecoder and MediaEncoder to open a LibAV device/stream.
rational< double > framerate