Ring Daemon 16.0.0
Loading...
Searching...
No Matches
media_decoder.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "libav_deps.h" // MUST BE INCLUDED FIRST
19#include "media_decoder.h"
20#include "media_device.h"
21#include "media_buffer.h"
22#include "media_const.h"
23#include "media_io_handle.h"
24#include "audio/ringbuffer.h"
25#include "audio/resampler.h"
27#include "decoder_finder.h"
28#include "manager.h"
29
30#ifdef RING_ACCEL
31#include "video/accel.h"
32#endif
33
34#include "string_utils.h"
35#include "logger.h"
36#include "client/ring_signal.h"
37
38#include <iostream>
39#include <unistd.h>
40#include <thread> // hardware_concurrency
41#include <chrono>
42#include <algorithm>
43
44namespace jami {
45
46// maximum number of packets the jitter buffer can queue
47const unsigned jitterBufferMaxSize_ {1500};
48// maximum time a packet can be queued
49const constexpr auto jitterBufferMaxDelay_ = std::chrono::milliseconds(50);
50// maximum number of times accelerated decoding can fail in a row before falling back to software
51const constexpr unsigned MAX_ACCEL_FAILURES {5};
52
54 : inputCtx_(avformat_alloc_context())
55 , startTime_(AV_NOPTS_VALUE)
56{}
57
59{
60 if (inputCtx_)
61 avformat_close_input(&inputCtx_);
62 av_dict_free(&options_);
63}
64
65const char*
67{
68 switch (status) {
69 case Status::Success:
70 return "Success";
72 return "End of file";
74 return "Read overflow";
76 return "Read error";
78 return "Fallback";
80 return "Restart required";
81 default:
82 return "Undefined";
83 }
84}
85
86int
88{
89 inputParams_ = params;
90 auto iformat = av_find_input_format(params.format.c_str());
91
92 if (!iformat && !params.format.empty())
93 JAMI_WARN("Unable to find format \"%s\"", params.format.c_str());
94
95 std::string input;
96
97 if (params.input == "pipewiregrab") {
98 //
99 // We rely on pipewiregrab for screen/window sharing on Wayland.
100 // Because pipewiregrab is a "video source filter" (part of FFmpeg's libavfilter
101 // library), its options must all be passed as part of the `input` string.
102 //
103 input = fmt::format("pipewiregrab=draw_mouse=1:fd={}:node={}", params.fd, params.node);
104 JAMI_LOG("Attempting to open input {}", input);
105 //
106 // In all other cases, we use the `options_` AVDictionary to pass options to FFmpeg.
107 //
108 // NOTE: We rely on the "lavfi" virtual input device to read pipewiregrab's output
109 // and create a corresponding stream (cf. the getDeviceParams function in
110 // daemon/src/media/video/v4l2/video_device_impl.cpp). The `options_` dictionary
111 // could be used to set lavfi's parameters if that was ever needed, but it isn't at
112 // the moment. (Doc: https://ffmpeg.org/ffmpeg-devices.html#lavfi)
113 //
114 } else {
115 if (params.width and params.height) {
116 auto sizeStr = fmt::format("{}x{}", params.width, params.height);
117 av_dict_set(&options_, "video_size", sizeStr.c_str(), 0);
118 }
119
120 if (params.framerate) {
121#ifdef _WIN32
122 // On windows, framerate settings don't reduce to avrational values
123 // that correspond to valid video device formats.
124 // e.g. A the rational<double>(10000000, 333333) or 30.000030000
125 // will be reduced by av_reduce to 999991/33333 or 30.00003000003
126 // which cause the device opening routine to fail.
127 // So we treat this imprecise reduction and adjust the value,
128 // or let dshow choose the framerate, which is, unfortunately,
129 // NOT the highest according to our experimentations.
130 auto framerate {params.framerate.real()};
131 framerate = params.framerate.numerator() / (params.framerate.denominator() + 0.5);
132 if (params.framerate.denominator() != 4999998)
133 av_dict_set(&options_, "framerate", jami::to_string(framerate).c_str(), 0);
134#else
135 av_dict_set(&options_, "framerate", jami::to_string(params.framerate.real()).c_str(), 0);
136#endif
137 }
138
139 if (params.offset_x || params.offset_y) {
140 av_dict_set(&options_, "offset_x", std::to_string(params.offset_x).c_str(), 0);
141 av_dict_set(&options_, "offset_y", std::to_string(params.offset_y).c_str(), 0);
142 }
143 if (params.channel)
144 av_dict_set(&options_, "channel", std::to_string(params.channel).c_str(), 0);
145 av_dict_set(&options_, "loop", params.loop.c_str(), 0);
146 av_dict_set(&options_, "sdp_flags", params.sdp_flags.c_str(), 0);
147
148 // Set jitter buffer options
149 av_dict_set(&options_, "reorder_queue_size", std::to_string(jitterBufferMaxSize_).c_str(), 0);
150 auto us = std::chrono::duration_cast<std::chrono::microseconds>(jitterBufferMaxDelay_).count();
151 av_dict_set(&options_, "max_delay", std::to_string(us).c_str(), 0);
152
153 if (!params.pixel_format.empty()) {
154 av_dict_set(&options_, "pixel_format", params.pixel_format.c_str(), 0);
155 }
156 if (!params.window_id.empty()) {
157 av_dict_set(&options_, "window_id", params.window_id.c_str(), 0);
158 }
159 av_dict_set(&options_, "draw_mouse", "1", 0);
160 av_dict_set(&options_, "is_area", std::to_string(params.is_area).c_str(), 0);
161
162#if defined(__APPLE__) && TARGET_OS_MAC
163 input = params.name;
164#else
165 input = params.input;
166#endif
167
168 JAMI_LOG("Attempting to open input {} with format {}, pixel format {}, size {}x{}, rate {}",
169 input,
170 params.format,
171 params.pixel_format,
172 params.width,
173 params.height,
174 params.framerate.real());
175 }
176
177 // Ask FFmpeg to open the input using the options set above
178 if (params.disable_dts_probe_delay && params.format == "sdp") {
179 av_opt_set_int(inputCtx_, "max_ts_probe", 0, AV_OPT_SEARCH_CHILDREN);
180 av_opt_set_int(inputCtx_, "fpsprobesize", 0, AV_OPT_SEARCH_CHILDREN);
181 } else {
182 // Don't waste time fetching framerate when finding stream info
183 av_opt_set_int(inputCtx_, "fpsprobesize", 1, AV_OPT_SEARCH_CHILDREN);
184 }
185
186 int ret = avformat_open_input(&inputCtx_, input.c_str(), iformat, options_ ? &options_ : NULL);
187
188 if (ret) {
189 JAMI_ERROR("avformat_open_input failed: {}", libav_utils::getError(ret));
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}",
194 params.format, baseWidth_, baseHeight_);
195 }
196
197 return ret;
198}
199
202{
203 return inputCtx_->duration;
204}
205
206bool
208{
209 if (av_seek_frame(inputCtx_, -1, timestamp, AVSEEK_FLAG_BACKWARD) >= 0) {
210 clearFrames();
211 return true;
212 }
213 return false;
214}
215
216void
218{
219 if (not streamInfoFound_) {
220 inputCtx_->max_analyze_duration = 30 * AV_TIME_BASE;
221 int err;
222 if ((err = avformat_find_stream_info(inputCtx_, nullptr)) < 0) {
223 JAMI_ERR() << "Unable to find stream info: " << libav_utils::getError(err);
224 }
225 streamInfoFound_ = true;
226 }
227}
228
229int
231{
232 auto sti = av_find_best_stream(inputCtx_, type, -1, -1, nullptr, 0);
233 if (type == AVMEDIA_TYPE_VIDEO && sti >= 0) {
234 auto st = inputCtx_->streams[sti];
235 auto disposition = st->disposition;
237 JAMI_DBG("Skipping attached picture stream");
238 sti = -1;
239 }
240 }
241 return sti;
242}
243
244void
246{
247 if (cb) {
248 inputCtx_->interrupt_callback.callback = cb;
249 inputCtx_->interrupt_callback.opaque = opaque;
250 } else {
251 inputCtx_->interrupt_callback.callback = 0;
252 }
253}
254void
255MediaDemuxer::setNeedFrameCb(std::function<void()> cb)
256{
257 needFrameCb_ = std::move(cb);
258}
259
260void
261MediaDemuxer::setFileFinishedCb(std::function<void(bool)> cb)
262{
263 fileFinishedCb_ = std::move(cb);
264}
265
266void
267MediaDemuxer::clearFrames()
268{
269 {
270 std::lock_guard lk {videoBufferMutex_};
271 while (!videoBuffer_.empty()) {
272 videoBuffer_.pop();
273 }
274 }
275 {
276 std::lock_guard lk {audioBufferMutex_};
277 while (!audioBuffer_.empty()) {
278 audioBuffer_.pop();
279 }
280 }
281}
282
283bool
285{
286 if (isAudio) {
287 return pushFrameFrom(audioBuffer_, isAudio, audioBufferMutex_);
288 } else {
289 return pushFrameFrom(videoBuffer_, isAudio, videoBufferMutex_);
290 }
291}
292
293bool
294MediaDemuxer::pushFrameFrom(
295 std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>>& buffer,
296 bool isAudio,
297 std::mutex& mutex)
298{
299 std::unique_lock lock(mutex);
300 if (buffer.empty()) {
301 if (currentState_ == MediaDemuxer::CurrentState::Finished) {
302 fileFinishedCb_(isAudio);
303 } else {
304 needFrameCb_();
305 }
306 return false;
307 }
308 auto packet = std::move(buffer.front());
309 if (!packet) {
310 return false;
311 }
312 auto streamIndex = packet->stream_index;
313 if (static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
314 return false;
315 }
316 if (auto& cb = streams_[streamIndex]) {
317 buffer.pop();
318 lock.unlock();
319 cb(*packet.get());
320 }
321 return true;
322}
323
326{
327 auto packet = std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>(av_packet_alloc(),
328 [](AVPacket* p) {
329 if (p)
331 &p);
332 });
333
334 int ret = av_read_frame(inputCtx_, packet.get());
335 if (ret == AVERROR(EAGAIN)) {
336 return Status::Success;
337 } else if (ret == AVERROR_EOF) {
338 return Status::EndOfFile;
339 } else if (ret < 0) {
340 JAMI_ERR("Unable to read frame: %s\n", libav_utils::getError(ret).c_str());
341 return Status::ReadError;
342 }
343
344 auto streamIndex = packet->stream_index;
345 if (static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
346 return Status::Success;
347 }
348
349 AVStream* stream = inputCtx_->streams[streamIndex];
350 if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
351 std::lock_guard lk {videoBufferMutex_};
352 videoBuffer_.push(std::move(packet));
353 if (videoBuffer_.size() >= 90) {
355 }
356 } else {
357 std::lock_guard lk {audioBufferMutex_};
358 audioBuffer_.push(std::move(packet));
359 if (audioBuffer_.size() >= 300) {
361 }
362 }
363 return Status::Success;
364}
365
366void
368{
369 inputCtx_->pb = ioctx->getContext();
370}
371
374{
375 if (inputParams_.format == "x11grab" || inputParams_.format == "dxgigrab") {
376 auto ret = inputCtx_->iformat->read_header(inputCtx_);
377 if (ret == AVERROR_EXTERNAL) {
378 JAMI_ERR("Unable to read frame: %s\n", libav_utils::getError(ret).c_str());
379 return Status::ReadError;
380 }
381 auto codecpar = inputCtx_->streams[0]->codecpar;
382 if (baseHeight_ != codecpar->height || baseWidth_ != codecpar->width) {
383 baseHeight_ = codecpar->height;
384 baseWidth_ = codecpar->width;
385 inputParams_.height = ((baseHeight_ >> 3) << 3);
386 inputParams_.width = ((baseWidth_ >> 3) << 3);
388 }
389 }
390
392 int ret = av_read_frame(inputCtx_, packet.get());
393 if (ret == AVERROR(EAGAIN)) {
394 /*no data available. Calculate time until next frame.
395 We do not use the emulated frame mechanism from the decoder because it will affect all
396 platforms. With the current implementation, the demuxer will be waiting just in case when
397 av_read_frame returns EAGAIN. For some platforms, av_read_frame is blocking and it will
398 never happen.
399 */
400 if (inputParams_.framerate.numerator() == 0)
401 return Status::Success;
402 rational<double> frameTime = 1e6 / inputParams_.framerate;
403 int64_t timeToSleep = lastReadPacketTime_ - av_gettime_relative()
404 + frameTime.real<int64_t>();
405 if (timeToSleep <= 0) {
406 return Status::Success;
407 }
408 std::this_thread::sleep_for(std::chrono::microseconds(timeToSleep));
409 return Status::Success;
410 } else if (ret == AVERROR_EOF) {
411 return Status::EndOfFile;
412 } else if (ret == AVERROR(EACCES)) {
414 } else if (ret < 0) {
415 auto media = inputCtx_->streams[0]->codecpar->codec_type;
416 const auto type = media == AVMediaType::AVMEDIA_TYPE_AUDIO
417 ? "AUDIO"
418 : (media == AVMediaType::AVMEDIA_TYPE_VIDEO ? "VIDEO" : "UNSUPPORTED");
419 JAMI_ERR("Unable to read [%s] frame: %s\n", type, libav_utils::getError(ret).c_str());
420 return Status::ReadError;
421 }
422
423 auto streamIndex = packet->stream_index;
424 if (static_cast<unsigned>(streamIndex) >= streams_.size() || streamIndex < 0) {
425 return Status::Success;
426 }
427
428 lastReadPacketTime_ = av_gettime_relative();
429
430 auto& cb = streams_[streamIndex];
431 if (cb) {
432 DecodeStatus ret = cb(*packet.get());
434 return Status::FallBack;
435 }
436 return Status::Success;
437}
438
439MediaDecoder::MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index)
440 : demuxer_(demuxer)
441 , avStream_(demuxer->getStream(index))
442{
443 demuxer->setStreamCallback(index, [this](AVPacket& packet) { return decode(packet); });
444 setupStream();
445}
446
447MediaDecoder::MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer,
448 int index,
450 : demuxer_(demuxer)
451 , avStream_(demuxer->getStream(index))
452 , callback_(std::move(observer))
453{
454 demuxer->setStreamCallback(index, [this](AVPacket& packet) { return decode(packet); });
455 setupStream();
456}
457
458bool
460{
461 return demuxer_->emitFrame(isAudio);
462}
463
467
469 : demuxer_(new MediaDemuxer)
470 , callback_(std::move(o))
471{}
472
474{
475#ifdef RING_ACCEL
476 if (decoderCtx_ && decoderCtx_->hw_device_ctx)
477 av_buffer_unref(&decoderCtx_->hw_device_ctx);
478#endif
479 if (decoderCtx_)
480 avcodec_free_context(&decoderCtx_);
481}
482
483void
488
489int
491{
492 return demuxer_->openInput(p);
493}
494
495void
497{
498 demuxer_->setInterruptCallback(cb, opaque);
499}
500
501void
503{
504 demuxer_->setIOContext(ioctx);
505}
506
507int
509{
510 demuxer_->findStreamInfo();
511 auto stream = demuxer_->selectStream(type);
512 if (stream < 0) {
513 JAMI_ERR("No stream found for type %i", static_cast<int>(type));
514 return -1;
515 }
516 avStream_ = demuxer_->getStream(stream);
517 if (avStream_ == nullptr) {
518 JAMI_ERR("No stream found at index %i", stream);
519 return -1;
520 }
521 demuxer_->setStreamCallback(stream, [this](AVPacket& packet) { return decode(packet); });
522 return setupStream();
523}
524
525int
526MediaDecoder::setupStream()
527{
528 int ret = 0;
529 avcodec_free_context(&decoderCtx_);
530
531 if (prepareDecoderContext() < 0)
532 return -1; // failed
533
534#ifdef RING_ACCEL
535 // if there was a fallback to software decoding, do not enable accel
536 // it has been disabled already by the video_receive_thread/video_input
537 enableAccel_ &= Manager::instance().videoPreferences.getDecodingAccelerated();
538
539 if (enableAccel_ and not fallback_) {
540 auto APIs = video::HardwareAccel::getCompatibleAccel(decoderCtx_->codec_id,
541 decoderCtx_->width,
542 decoderCtx_->height,
544 for (const auto& it : APIs) {
545 accel_ = std::make_unique<video::HardwareAccel>(it); // save accel
546 auto ret = accel_->initAPI(false, nullptr);
547 if (ret < 0) {
548 accel_.reset();
549 continue;
550 }
551 if (prepareDecoderContext() < 0)
552 return -1; // failed
553 accel_->setDetails(decoderCtx_);
554 decoderCtx_->opaque = accel_.get();
555 decoderCtx_->pix_fmt = accel_->getFormat();
556 if (avcodec_open2(decoderCtx_, inputDecoder_, &options_) < 0) {
557 // Failed to open codec
558 JAMI_WARN("Fail to open hardware decoder for %s with %s",
559 avcodec_get_name(decoderCtx_->codec_id),
560 it.getName().c_str());
561 avcodec_free_context(&decoderCtx_);
562 decoderCtx_ = nullptr;
563 accel_.reset();
564 continue;
565 } else {
566 // Succeed to open codec
567 JAMI_WARN("Using hardware decoding for %s with %s",
568 avcodec_get_name(decoderCtx_->codec_id),
569 it.getName().c_str());
570 break;
571 }
572 }
573 }
574#endif
575
576 JAMI_LOG("Using {} ({}) decoder for {}",
577 inputDecoder_->long_name,
578 inputDecoder_->name,
579 av_get_media_type_string(avStream_->codecpar->codec_type));
580 decoderCtx_->thread_count = std::max(1u, std::min(8u, std::thread::hardware_concurrency() / 2));
581 if (emulateRate_)
582 JAMI_DBG() << "Using framerate emulation";
583 startTime_ = av_gettime(); // used to set pts after decoding, and for rate emulation
584
585#ifdef RING_ACCEL
586 if (!accel_) {
587 JAMI_WARN("Not using hardware decoding for %s", avcodec_get_name(decoderCtx_->codec_id));
588 ret = avcodec_open2(decoderCtx_, inputDecoder_, nullptr);
589 }
590#else
591 ret = avcodec_open2(decoderCtx_, inputDecoder_, nullptr);
592#endif
593 if (ret < 0) {
594 JAMI_ERR() << "Unable to open codec: " << libav_utils::getError(ret);
595 return -1;
596 }
597
598 return 0;
599}
600
601int
602MediaDecoder::prepareDecoderContext()
603{
604 inputDecoder_ = findDecoder(avStream_->codecpar->codec_id);
605 if (!inputDecoder_) {
606 JAMI_ERROR("Unsupported codec");
607 return -1;
608 }
609
610 decoderCtx_ = avcodec_alloc_context3(inputDecoder_);
611 if (!decoderCtx_) {
612 JAMI_ERROR("Failed to create decoder context");
613 return -1;
614 }
615 avcodec_parameters_to_context(decoderCtx_, avStream_->codecpar);
616 width_ = decoderCtx_->width;
617 height_ = decoderCtx_->height;
618 decoderCtx_->framerate = avStream_->avg_frame_rate;
619 if (avStream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
620 if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
621 decoderCtx_->framerate = inputParams_.framerate;
622 if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
623 decoderCtx_->framerate = {30, 1};
624 }
625 else if (avStream_->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
626 if (decoderCtx_->codec_id == AV_CODEC_ID_OPUS) {
627 av_opt_set_int(decoderCtx_, "decode_fec", fecEnabled_ ? 1 : 0, AV_OPT_SEARCH_CHILDREN);
628 }
629 auto format = libav_utils::choose_sample_fmt_default(inputDecoder_, Manager::instance().getRingBufferPool().getInternalAudioFormat().sampleFormat);
630 decoderCtx_->sample_fmt = format;
631 decoderCtx_->request_sample_fmt = format;
632 }
633 return 0;
634}
635
636void
641
644{
645 int frameFinished = 0;
646 auto ret = avcodec_send_packet(decoderCtx_, &packet);
647 // TODO: Investigate avcodec_send_packet returning AVERROR_INVALIDDATA.
648 // * Bug Windows documented here: git.jami.net/savoirfairelinux/jami-daemon/-/issues/1116
649 // where avcodec_send_packet returns AVERROR_INVALIDDATA when the size information in the
650 // packet is incorrect. Falling back onto sw decoding in this causes a segfault.
651 // * A second problem occurs on some Windows devices with intel CPUs in which hardware
652 // decoding fails with AVERROR_INVALIDDATA when using H.264. However, in this scenario,
653 // falling back to software decoding works fine.
654 // We need to figure out why this behavior occurs and how to discriminate between the two.
655 if (ret < 0 && ret != AVERROR(EAGAIN)) {
656#ifdef RING_ACCEL
657 if (accel_) {
658 JAMI_WARN("Decoding error falling back to software");
659 fallback_ = true;
660 accel_.reset();
661 avcodec_flush_buffers(decoderCtx_);
662 setupStream();
664 }
665#endif
666 avcodec_flush_buffers(decoderCtx_);
668 }
669
670#ifdef ENABLE_VIDEO
671 auto f = (inputDecoder_->type == AVMEDIA_TYPE_VIDEO)
672 ? std::static_pointer_cast<MediaFrame>(std::make_shared<VideoFrame>())
674#else
675 auto f = std::static_pointer_cast<MediaFrame>(std::make_shared<AudioFrame>());
676#endif
677 auto frame = f->pointer();
678 ret = avcodec_receive_frame(decoderCtx_, frame);
679 // time_base is not set in AVCodecContext for decoding
680 // fail to set it causes pts to be incorrectly computed down in the function
681 if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO) {
682 decoderCtx_->time_base.num = decoderCtx_->framerate.den;
683 decoderCtx_->time_base.den = decoderCtx_->framerate.num;
684 } else {
685 decoderCtx_->time_base.num = 1;
686 decoderCtx_->time_base.den = decoderCtx_->sample_rate;
687 }
688 frame->time_base = decoderCtx_->time_base;
689 if (resolutionChangedCallback_) {
690 if (decoderCtx_->width != width_ or decoderCtx_->height != height_) {
691 JAMI_DBG("Resolution changed from %dx%d to %dx%d",
692 width_,
693 height_,
694 decoderCtx_->width,
695 decoderCtx_->height);
696 width_ = decoderCtx_->width;
697 height_ = decoderCtx_->height;
698 resolutionChangedCallback_(width_, height_);
699 }
700 }
701 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
703 }
704 if (ret >= 0)
705 frameFinished = 1;
706
707 if (frameFinished) {
708 if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO)
709 frame->format = (AVPixelFormat) correctPixFmt(frame->format);
710 auto packetTimestamp = frame->pts; // in stream time base
711 frame->pts = av_rescale_q_rnd(av_gettime() - startTime_,
712 {1, AV_TIME_BASE},
713 decoderCtx_->time_base,
714 static_cast<AVRounding>(AV_ROUND_NEAR_INF
716 lastTimestamp_ = frame->pts;
717 if (emulateRate_ and packetTimestamp != AV_NOPTS_VALUE) {
718 auto startTime = avStream_->start_time == AV_NOPTS_VALUE ? 0 : avStream_->start_time;
719 rational<double> frame_time = rational<double>(getTimeBase())
720 * rational<double>(packetTimestamp - startTime);
721 auto target_relative = static_cast<std::int64_t>(frame_time.real() * 1e6);
722 auto target_absolute = startTime_ + target_relative;
723 if (target_relative < seekTime_) {
725 }
726 // required frame found. Reset seek time
727 if (target_relative >= seekTime_) {
728 resetSeekTime();
729 }
730 auto now = av_gettime();
731 if (target_absolute > now) {
732 std::this_thread::sleep_for(std::chrono::microseconds(target_absolute - now));
733 }
734 }
735
736 if (callback_)
737 callback_(std::move(f));
738
739 if (contextCallback_ && firstDecode_.load()) {
740 firstDecode_.exchange(false);
741 contextCallback_();
742 }
744 }
746}
747
748void
750{
751 seekTime_ = time;
752}
753
756{
757 auto ret = demuxer_->decode();
759 avcodec_flush_buffers(decoderCtx_);
760 setupStream();
762 }
763 return ret;
764}
765
766#ifdef ENABLE_VIDEO
767#ifdef RING_ACCEL
768void
769MediaDecoder::enableAccel(bool enableAccel)
770{
773 if (!enableAccel) {
774 accel_.reset();
775 if (decoderCtx_)
776 decoderCtx_->opaque = nullptr;
777 }
778}
779#endif
780
783{
786
787 int frameFinished = 0;
788 int ret = 0;
789 ret = avcodec_send_packet(decoderCtx_, &inpacket);
790 if (ret < 0 && ret != AVERROR(EAGAIN))
792
793 auto result = std::make_shared<MediaFrame>();
794 ret = avcodec_receive_frame(decoderCtx_, result->pointer());
795 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
797 if (ret >= 0)
798 frameFinished = 1;
799
800 if (frameFinished) {
802 if (callback_)
803 callback_(std::move(result));
805 }
806
808}
809#endif // ENABLE_VIDEO
810
811int
813{
814 return decoderCtx_ ? decoderCtx_->width : 0;
815}
816
817int
819{
820 return decoderCtx_ ? decoderCtx_->height : 0;
821}
822
823std::string
825{
826 return decoderCtx_ ? decoderCtx_->codec->name : "";
827}
828
831{
832 return {(double) avStream_->avg_frame_rate.num, (double) avStream_->avg_frame_rate.den};
833}
834
836MediaDecoder::getTimeBase() const
837{
838 return {(unsigned) avStream_->time_base.num, (unsigned) avStream_->time_base.den};
839}
840
843{
844 return decoderCtx_->pix_fmt;
845}
846
847int
848MediaDecoder::correctPixFmt(int input_pix_fmt)
849{
850 // https://ffmpeg.org/pipermail/ffmpeg-user/2014-February/020152.html
851 int pix_fmt;
852 switch (input_pix_fmt) {
855 break;
858 break;
861 break;
864 break;
865 default:
867 break;
868 }
869 return pix_fmt;
870}
871
872MediaStream
873MediaDecoder::getStream(std::string name) const
874{
875 if (!decoderCtx_) {
876 JAMI_WARN("No decoder context");
877 return {};
878 }
879 auto ms = MediaStream(name, decoderCtx_, lastTimestamp_);
880#ifdef RING_ACCEL
881 // accel_ is null if not using accelerated codecs
882 if (accel_)
883 ms.format = accel_->getSoftwareFormat();
884#endif
885 return ms;
886}
887
888} // namespace jami
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:676
DecodeStatus flush()
void updateStartTime(int64_t startTime)
AVDictionary * options_
MediaStream getStream(std::string name="") const
int setup(AVMediaType type)
void setIOContext(MediaIOHandle *ioctx)
int openInput(const DeviceParams &)
bool emitFrame(bool isAudio)
void setInterruptCallback(int(*cb)(void *), void *opaque)
MediaDemuxer::Status decode()
rational< double > getFps() const
AVPixelFormat getPixelFormat() const
void setSeekTime(int64_t time)
std::string getDecoderName() const
int openInput(const DeviceParams &)
void setInterruptCallback(int(*cb)(void *), void *opaque)
void setIOContext(MediaIOHandle *ioctx)
static const char * getStatusStr(Status status)
bool seekFrame(int stream_index, int64_t timestamp)
int64_t getDuration() const
int selectStream(AVMediaType type)
bool emitFrame(bool isAudio)
void setNeedFrameCb(std::function< void()> cb)
void setFileFinishedCb(std::function< void(bool)> cb)
Naive implementation of the boost::rational interface, described here: https://www....
Definition rational.h:39
constexpr I numerator() const
Definition rational.h:86
static std::list< HardwareAccel > getCompatibleAccel(AVCodecID id, int width, int height, CodecType type)
Definition accel.cpp:424
void av_buffer_unref(AVBufferRef **buf)
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_ERROR(formatstr,...)
Definition logger.h:228
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARN(...)
Definition logger.h:217
#define JAMI_LOG(formatstr,...)
Definition logger.h:225
AVSampleFormat choose_sample_fmt_default(const AVCodec *codec, AVSampleFormat defaultFormat)
std::string getError(int err)
const constexpr unsigned MAX_ACCEL_FAILURES
void emitSignal(Args... args)
Definition ring_signal.h:64
const constexpr auto jitterBufferMaxDelay_
const unsigned jitterBufferMaxSize_
std::function< void(std::shared_ptr< MediaFrame > &&)> MediaObserver
std::string to_string(double value)
@ CODEC_DECODER
Definition media_codec.h:41
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.
std::string format
rational< double > framerate
void av_packet_free(AVPacket **frame)