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 if (ret < 0) {
648 // In a case caught on Windows, avcodec_send_packet returns AVERROR_INVALIDDATA when
649 // the size information in the packet is incorrect. This is a workaround to handle this
650 // case which was observed with Logitech C920 camera, and presented similar symptoms when
651 // the camera was opened with other applications like Chrome, Windows Camera app, etc.
652 // Experimentally, every second packet was affected.
653 if (ret == AVERROR_INVALIDDATA) {
654 JAMI_DEBUG("Invalid data in packet");
656 }
657 if (ret != AVERROR(EAGAIN)) {
658#ifdef RING_ACCEL
659 if (accel_) {
660 JAMI_WARN("Decoding error falling back to software");
661 fallback_ = true;
662 accel_.reset();
663 avcodec_flush_buffers(decoderCtx_);
664 setupStream();
666 }
667#endif
668 }
669 avcodec_flush_buffers(decoderCtx_);
671 }
672
673#ifdef ENABLE_VIDEO
674 auto f = (inputDecoder_->type == AVMEDIA_TYPE_VIDEO)
675 ? std::static_pointer_cast<MediaFrame>(std::make_shared<VideoFrame>())
677#else
678 auto f = std::static_pointer_cast<MediaFrame>(std::make_shared<AudioFrame>());
679#endif
680 auto frame = f->pointer();
681 ret = avcodec_receive_frame(decoderCtx_, frame);
682 // time_base is not set in AVCodecContext for decoding
683 // fail to set it causes pts to be incorrectly computed down in the function
684 if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO) {
685 decoderCtx_->time_base.num = decoderCtx_->framerate.den;
686 decoderCtx_->time_base.den = decoderCtx_->framerate.num;
687 } else {
688 decoderCtx_->time_base.num = 1;
689 decoderCtx_->time_base.den = decoderCtx_->sample_rate;
690 }
691 frame->time_base = decoderCtx_->time_base;
692 if (resolutionChangedCallback_) {
693 if (decoderCtx_->width != width_ or decoderCtx_->height != height_) {
694 JAMI_DBG("Resolution changed from %dx%d to %dx%d",
695 width_,
696 height_,
697 decoderCtx_->width,
698 decoderCtx_->height);
699 width_ = decoderCtx_->width;
700 height_ = decoderCtx_->height;
701 resolutionChangedCallback_(width_, height_);
702 }
703 }
704 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
706 }
707 if (ret >= 0)
708 frameFinished = 1;
709
710 if (frameFinished) {
711 if (inputDecoder_->type == AVMEDIA_TYPE_VIDEO)
712 frame->format = (AVPixelFormat) correctPixFmt(frame->format);
713 auto packetTimestamp = frame->pts; // in stream time base
714 frame->pts = av_rescale_q_rnd(av_gettime() - startTime_,
715 {1, AV_TIME_BASE},
716 decoderCtx_->time_base,
717 static_cast<AVRounding>(AV_ROUND_NEAR_INF
719 lastTimestamp_ = frame->pts;
720 if (emulateRate_ and packetTimestamp != AV_NOPTS_VALUE) {
721 auto startTime = avStream_->start_time == AV_NOPTS_VALUE ? 0 : avStream_->start_time;
722 rational<double> frame_time = rational<double>(getTimeBase())
723 * rational<double>(packetTimestamp - startTime);
724 auto target_relative = static_cast<std::int64_t>(frame_time.real() * 1e6);
725 auto target_absolute = startTime_ + target_relative;
726 if (target_relative < seekTime_) {
728 }
729 // required frame found. Reset seek time
730 if (target_relative >= seekTime_) {
731 resetSeekTime();
732 }
733 auto now = av_gettime();
734 if (target_absolute > now) {
735 std::this_thread::sleep_for(std::chrono::microseconds(target_absolute - now));
736 }
737 }
738
739 if (callback_)
740 callback_(std::move(f));
741
742 if (contextCallback_ && firstDecode_.load()) {
743 firstDecode_.exchange(false);
744 contextCallback_();
745 }
747 }
749}
750
751void
753{
754 seekTime_ = time;
755}
756
759{
760 auto ret = demuxer_->decode();
762 avcodec_flush_buffers(decoderCtx_);
763 setupStream();
765 }
766 return ret;
767}
768
769#ifdef ENABLE_VIDEO
770#ifdef RING_ACCEL
771void
772MediaDecoder::enableAccel(bool enableAccel)
773{
776 if (!enableAccel) {
777 accel_.reset();
778 if (decoderCtx_)
779 decoderCtx_->opaque = nullptr;
780 }
781}
782#endif
783
786{
789
790 int frameFinished = 0;
791 int ret = 0;
792 ret = avcodec_send_packet(decoderCtx_, &inpacket);
793 if (ret < 0 && ret != AVERROR(EAGAIN))
795
796 auto result = std::make_shared<MediaFrame>();
797 ret = avcodec_receive_frame(decoderCtx_, result->pointer());
798 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
800 if (ret >= 0)
801 frameFinished = 1;
802
803 if (frameFinished) {
805 if (callback_)
806 callback_(std::move(result));
808 }
809
811}
812#endif // ENABLE_VIDEO
813
814int
816{
817 return decoderCtx_ ? decoderCtx_->width : 0;
818}
819
820int
822{
823 return decoderCtx_ ? decoderCtx_->height : 0;
824}
825
826std::string
828{
829 return decoderCtx_ ? decoderCtx_->codec->name : "";
830}
831
834{
835 return {(double) avStream_->avg_frame_rate.num, (double) avStream_->avg_frame_rate.den};
836}
837
839MediaDecoder::getTimeBase() const
840{
841 return {(unsigned) avStream_->time_base.num, (unsigned) avStream_->time_base.den};
842}
843
846{
847 return decoderCtx_->pix_fmt;
848}
849
850int
851MediaDecoder::correctPixFmt(int input_pix_fmt)
852{
853 // https://ffmpeg.org/pipermail/ffmpeg-user/2014-February/020152.html
854 int pix_fmt;
855 switch (input_pix_fmt) {
858 break;
861 break;
864 break;
867 break;
868 default:
870 break;
871 }
872 return pix_fmt;
873}
874
875MediaStream
876MediaDecoder::getStream(std::string name) const
877{
878 if (!decoderCtx_) {
879 JAMI_WARN("No decoder context");
880 return {};
881 }
882 auto ms = MediaStream(name, decoderCtx_, lastTimestamp_);
883#ifdef RING_ACCEL
884 // accel_ is null if not using accelerated codecs
885 if (accel_)
886 ms.format = accel_->getSoftwareFormat();
887#endif
888 return ms;
889}
890
891} // 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: http://www....
Definition rational.h:38
constexpr I numerator() const
Definition rational.h:77
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_DEBUG(formatstr,...)
Definition logger.h:226
#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)