40#include <asio/steady_timer.hpp>
56 if (streamInfoTimer_) {
57 streamInfoTimer_->cancel();
58 streamInfoTimer_.reset();
74 return "Read overflow";
80 return "Restart required";
97 if (
params.input ==
"pipewiregrab") {
103 input = fmt::format(
"pipewiregrab=draw_mouse=1:fd={}:node={}",
params.fd,
params.node);
104 JAMI_LOG(
"Attempting to open input {}", input);
130 auto framerate {
params.framerate.real()};
131 framerate =
params.framerate.numerator() / (
params.framerate.denominator() + 0.5);
132 if (
params.framerate.denominator() != 4999998)
153 if (!
params.pixel_format.empty()) {
156 if (!
params.window_id.empty()) {
162#if defined(__APPLE__) && TARGET_OS_MAC
168 JAMI_LOG(
"Attempting to open input {} with format {}, pixel format {}, size {}x{}, rate {}",
178 if (
params.disable_dts_probe_delay &&
params.format ==
"sdp") {
190 }
else if (inputCtx_->nb_streams > 0 && inputCtx_->streams[0]->codecpar) {
191 baseWidth_ = inputCtx_->streams[0]->codecpar->width;
192 baseHeight_ = inputCtx_->streams[0]->codecpar->height;
193 JAMI_LOG(
"Opened input using format {:s} and resolution {:d}x{:d}",
params.format, baseWidth_, baseHeight_);
202 return inputCtx_->duration;
208 std::lock_guard
lk(inputCtxMutex_);
219 if (
not streamInfoFound_) {
222 if (!streamInfoTimer_)
223 streamInfoTimer_ = std::make_unique<asio::steady_timer>(*
Manager::instance().ioContext());
224 streamInfoTimer_->expires_after(std::chrono::milliseconds(1500));
225 streamInfoTimer_->async_wait([weak =
weak_from_this()](
const std::error_code&
ec) {
228 if (
auto self = weak.lock()) {
229 if (!self->streamInfoFound_) {
230 JAMI_LOG(
"findStreamInfo: 1500ms elapsed, requesting keyframe to aid probing");
231 if (self->keyFrameRequestCb_)
232 self->keyFrameRequestCb_();
242 streamInfoFound_ =
true;
243 if (streamInfoTimer_) {
244 streamInfoTimer_->cancel();
245 streamInfoTimer_.reset();
251MediaDemuxer::selectStream(AVMediaType type)
253 auto sti = av_find_best_stream(inputCtx_, type, -1, -1,
nullptr, 0);
254 if (type == AVMEDIA_TYPE_VIDEO && sti >= 0) {
255 auto* st = inputCtx_->streams[sti];
256 auto disposition = st->disposition;
257 if (disposition & AV_DISPOSITION_ATTACHED_PIC) {
258 JAMI_DBG(
"Skipping attached picture stream");
266MediaDemuxer::setInterruptCallback(
int (*cb)(
void*),
void* opaque)
269 inputCtx_->interrupt_callback.callback = cb;
270 inputCtx_->interrupt_callback.opaque = opaque;
272 inputCtx_->interrupt_callback.callback = 0;
276MediaDemuxer::setNeedFrameCb(std::function<
void()> cb)
278 needFrameCb_ = std::move(cb);
282MediaDemuxer::setFileFinishedCb(std::function<
void(
bool)> cb)
284 fileFinishedCb_ = std::move(cb);
288MediaDemuxer::setKeyFrameRequestCb(std::function<
void()> cb)
290 keyFrameRequestCb_ = std::move(cb);
294MediaDemuxer::clearFrames()
297 std::lock_guard lk {videoBufferMutex_};
298 while (!videoBuffer_.empty()) {
303 std::lock_guard lk {audioBufferMutex_};
304 while (!audioBuffer_.empty()) {
311MediaDemuxer::emitFrame(
bool isAudio)
314 return pushFrameFrom(audioBuffer_, isAudio, audioBufferMutex_);
316 return pushFrameFrom(videoBuffer_, isAudio, videoBufferMutex_);
321MediaDemuxer::pushFrameFrom(std::queue<std::unique_ptr<AVPacket, std::function<
void(AVPacket*)>>>& buffer,
325 std::unique_lock lock(mutex);
326 if (buffer.empty()) {
327 if (currentState_ == MediaDemuxer::CurrentState::Finished) {
328 fileFinishedCb_(isAudio);
334 auto packet = std::move(buffer.front());
338 auto streamIndex = packet->stream_index;
339 if (
static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
342 if (
auto& cb = streams_[streamIndex]) {
351MediaDemuxer::demuxe()
353 auto packet = std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>(av_packet_alloc(), [](AVPacket* p) {
360 std::lock_guard lk(inputCtxMutex_);
361 int ret = av_read_frame(inputCtx_, packet.get());
362 if (ret == AVERROR(EAGAIN)) {
363 return Status::Success;
364 }
else if (ret == AVERROR_EOF) {
365 return Status::EndOfFile;
366 }
else if (ret < 0) {
367 JAMI_ERR(
"Unable to read frame: %s\n", libav_utils::getError(ret).c_str());
368 return Status::ReadError;
371 auto streamIndex = packet->stream_index;
372 if (
static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
373 return Status::Success;
376 isVideo = inputCtx_->streams[streamIndex]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
380 std::lock_guard lk {videoBufferMutex_};
381 videoBuffer_.push(std::move(packet));
382 if (videoBuffer_.size() >= 90) {
383 return Status::ReadBufferOverflow;
386 std::lock_guard lk {audioBufferMutex_};
387 audioBuffer_.push(std::move(packet));
388 if (audioBuffer_.size() >= 300) {
389 return Status::ReadBufferOverflow;
392 return Status::Success;
402MediaDemuxer::decode()
404 if (inputParams_.format ==
"x11grab" || inputParams_.format ==
"dxgigrab") {
405 auto ret = inputCtx_->iformat->read_header(inputCtx_);
406 if (ret == AVERROR_EXTERNAL) {
407 JAMI_ERR(
"Unable to read frame: %s\n", libav_utils::getError(ret).c_str());
408 return Status::ReadError;
410 auto* codecpar = inputCtx_->streams[0]->codecpar;
411 if (baseHeight_ != codecpar->height || baseWidth_ != codecpar->width) {
412 baseHeight_ = codecpar->height;
413 baseWidth_ = codecpar->width;
414 inputParams_.height = ((baseHeight_ >> 3) << 3);
415 inputParams_.width = ((baseWidth_ >> 3) << 3);
416 return Status::RestartRequired;
421 int ret = av_read_frame(inputCtx_, packet.get());
422 if (ret == AVERROR(EAGAIN)) {
429 if (inputParams_.framerate.numerator() == 0)
430 return Status::Success;
432 int64_t timeToSleep = lastReadPacketTime_ - av_gettime_relative() + frameTime.
real<int64_t>();
433 if (timeToSleep <= 0) {
434 return Status::Success;
436 std::this_thread::sleep_for(std::chrono::microseconds(timeToSleep));
437 return Status::Success;
438 }
else if (ret == AVERROR_EOF) {
439 return Status::EndOfFile;
440 }
else if (ret == AVERROR(EACCES)) {
441 return Status::RestartRequired;
442 }
else if (ret < 0) {
443 auto media = inputCtx_->streams[0]->codecpar->codec_type;
444 const auto*
const type = media == AVMediaType::AVMEDIA_TYPE_AUDIO
446 : (media == AVMediaType::AVMEDIA_TYPE_VIDEO ?
"VIDEO" :
"UNSUPPORTED");
447 JAMI_ERR(
"Unable to read [%s] frame: %s\n", type, libav_utils::getError(ret).c_str());
448 return Status::ReadError;
451 auto streamIndex = packet->stream_index;
452 if (
static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
453 return Status::Success;
456 lastReadPacketTime_ = av_gettime_relative();
458 auto& cb = streams_[streamIndex];
461 if (ret == DecodeStatus::FallBack)
462 return Status::FallBack;
464 return Status::Success;
467MediaDecoder::MediaDecoder(
const std::shared_ptr<MediaDemuxer>& demuxer,
int index)
469 , avStream_(demuxer->getStream(index))
477 , avStream_(
demuxer->getStream(index))
487 return demuxer_->emitFrame(
isAudio);
502 if (decoderCtx_ && decoderCtx_->hw_device_ctx)
518 return demuxer_->openInput(p);
524 demuxer_->setInterruptCallback(
cb,
opaque);
530 demuxer_->setIOContext(
ioctx);
536 demuxer_->setKeyFrameRequestCb(std::move(
cb));
543 auto stream = demuxer_->selectStream(type);
545 JAMI_ERR(
"No stream found for type %i",
static_cast<int>(type));
548 avStream_ = demuxer_->getStream(stream);
549 if (avStream_ ==
nullptr) {
550 JAMI_ERR(
"No stream found at index %i", stream);
553 demuxer_->setStreamCallback(stream, [
this](
AVPacket& packet) {
return decode(packet); });
554 return setupStream();
558MediaDecoder::setupStream()
563 if (prepareDecoderContext() < 0)
576 for (
const auto&
it :
APIs) {
577 accel_ = std::make_unique<video::HardwareAccel>(
it);
578 auto ret =
accel_->initAPI(
false,
nullptr);
583 if (prepareDecoderContext() < 0)
585 accel_->setDetails(decoderCtx_);
586 decoderCtx_->opaque =
accel_.get();
587 decoderCtx_->pix_fmt =
accel_->getFormat();
590 JAMI_WARN(
"Fail to open hardware decoder for %s with %s",
592 it.getName().c_str());
594 decoderCtx_ =
nullptr;
599 JAMI_WARN(
"Using hardware decoding for %s with %s",
601 it.getName().c_str());
608 JAMI_LOG(
"Using {} ({}) decoder for {}",
609 inputDecoder_->long_name,
612 decoderCtx_->thread_count = std::max(1, std::min(8,
static_cast<int>(std::thread::hardware_concurrency()) / 2));
614 JAMI_DBG() <<
"Using framerate emulation";
634MediaDecoder::prepareDecoderContext()
636 inputDecoder_ =
findDecoder(avStream_->codecpar->codec_id);
637 if (!inputDecoder_) {
644 JAMI_ERROR(
"Failed to create decoder context");
648 decoderCtx_->pkt_timebase = avStream_->time_base;
649 width_ = decoderCtx_->width;
650 height_ = decoderCtx_->height;
651 decoderCtx_->framerate = avStream_->avg_frame_rate;
653 if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
654 decoderCtx_->framerate = inputParams_.
framerate;
655 if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
656 decoderCtx_->framerate = {30, 1};
662 inputDecoder_,
Manager::instance().getRingBufferPool().getInternalAudioFormat().sampleFormat);
663 decoderCtx_->sample_fmt = format;
664 decoderCtx_->request_sample_fmt = format;
691 JAMI_WARN(
"Decoding error falling back to software");
705 ? std::static_pointer_cast<MediaFrame>(std::make_shared<VideoFrame>())
708 auto f = std::static_pointer_cast<MediaFrame>(std::make_shared<AudioFrame>());
710 auto*
frame =
f->pointer();
715 decoderCtx_->time_base.num = decoderCtx_->framerate.den;
716 decoderCtx_->time_base.den = decoderCtx_->framerate.num;
718 decoderCtx_->time_base.num = 1;
719 decoderCtx_->time_base.den = decoderCtx_->sample_rate;
721 frame->time_base = decoderCtx_->time_base;
722 if (resolutionChangedCallback_) {
723 if (decoderCtx_->width != width_
or decoderCtx_->height != height_) {
724 JAMI_DBG(
"Resolution changed from %dx%d to %dx%d", width_, height_, decoderCtx_->width, decoderCtx_->height);
725 width_ = decoderCtx_->width;
726 height_ = decoderCtx_->height;
727 resolutionChangedCallback_(width_, height_);
753 decoderCtx_->time_base,
755 lastTimestamp_ =
frame->pts;
758 rational<double>
frame_time = rational<double>(getTimeBase())
776 callback_(std::move(
f));
778 if (contextCallback_ && firstDecode_.load()) {
779 firstDecode_.exchange(
false);
796 auto ret = demuxer_->decode();
815 decoderCtx_->opaque =
nullptr;
832 auto result = std::make_shared<MediaFrame>();
842 callback_(std::move(result));
853 return decoderCtx_ ? decoderCtx_->width : 0;
859 return decoderCtx_ ? decoderCtx_->height : 0;
865 return decoderCtx_ ? decoderCtx_->codec->name :
"";
871 return {(
double) avStream_->avg_frame_rate.num, (
double) avStream_->avg_frame_rate.den};
875MediaDecoder::getTimeBase()
const
877 return {(
unsigned) avStream_->time_base.num, (
unsigned) avStream_->time_base.den};
883 return decoderCtx_->pix_fmt;
918 auto ms =
MediaStream(name, decoderCtx_, lastTimestamp_);
922 ms.format =
accel_->getSoftwareFormat();
static LIBJAMI_TEST_EXPORT Manager & instance()
Naive implementation of the boost::rational interface, described here: https://www....
static std::list< HardwareAccel > getCompatibleAccel(AVCodecID id, int width, int height, CodecType type)
void av_buffer_unref(AVBufferRef **buf)
#define JAMI_ERROR(formatstr,...)
#define JAMI_LOG(formatstr,...)
AVSampleFormat choose_sample_fmt_default(const AVCodec *codec, AVSampleFormat defaultFormat)
std::string getError(int err)
void emitSignal(Args... args)
const constexpr auto jitterBufferMaxDelay_
const unsigned jitterBufferMaxSize_
std::function< void(std::shared_ptr< MediaFrame > &&)> MediaObserver
std::string to_string(double value)
libjami::MediaFrame MediaFrame
libjami::AudioFrame AudioFrame
const AVCodec * findDecoder(const enum AVCodecID codec_id)
Attempt to find standalone AVCodec decoder using AVCodecID, or fallback to the default decoder.
std::unique_ptr< AVPacket, AVPacket_deleter > PacketBuffer
DeviceParams Parameters used by MediaDecoder and MediaEncoder to open a LibAV device/stream.
rational< double > framerate
void av_packet_free(AVPacket **frame)