Ring Daemon
Loading...
Searching...
No Matches
debug_utils.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 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#pragma once
18
19#include "libav_deps.h"
20#include "media_buffer.h"
21
22#include <opendht/utils.h>
23#include <fmt/color.h>
24
25#include <algorithm>
26#include <chrono>
27#include <cstdio>
28#include <ratio>
29#include <string_view>
30
31#include "logger.h"
32
33#warning Debug utilities included in build
34
35using Clock = std::chrono::steady_clock;
36using namespace std::literals;
37
38namespace jami {
39namespace debug {
40
48class Timer
49{
50public:
51 Timer(std::string_view name)
52 : name_(name)
53 , start_(Clock::now())
54 {}
55
56 ~Timer() { print("end"sv); }
57
58 template<class Period = std::ratio<1>>
60 {
61 auto diff = std::chrono::duration_cast<Period>(Clock::now() - start_);
62 return diff.count();
63 }
64
65 void print(std::string_view action) const
66 {
67 JAMI_LOG("{}: {} after {}", name_, action, dht::print_duration(Clock::now() - start_));
68 }
69
70private:
71 std::string_view name_;
72 std::chrono::time_point<Clock> start_;
73};
74
75template<size_t N>
77{
78 consteval StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
79 char value[N];
80};
81
82/*
83 * Ex:
84 * StatsTimer<"TaskName"> timer;
85 * std::this_thread::sleep_for(std::chrono::milliseconds(10));
86 * // Timer automatically prints stats on destruction
87 */
88template<StringLiteral Tag>
90{
91 using time_point = std::chrono::time_point<Clock>;
92 using duration = Clock::duration;
93
94public:
96 : start_(Clock::now())
97 {}
98
100 {
101 auto dt = Clock::now() - start_;
102 auto stats = inc(dt);
103 if (stats.count > 1) {
104 JAMI_LOG("{}: end after {} - Average duration: {} ({}) Total: {} Min: {} Max: {}",
105 Tag.value,
106 dht::print_duration(dt),
107 fmt::styled(dht::print_duration(stats.total_duration / stats.count), fmt::emphasis::bold),
108 stats.count,
109 dht::print_duration(stats.total_duration),
110 dht::print_duration(stats.min_duration),
111 dht::print_duration(stats.max_duration));
112 } else {
113 JAMI_LOG("{}: end after {}", Tag.value, dht::print_duration(dt));
114 }
115 }
116
117private:
118 struct Stats
119 {
120 duration total_duration {};
121 duration min_duration {duration::max()};
122 duration max_duration {duration::min()};
123 duration::rep count {0};
124 };
125
126 time_point start_;
127 static inline std::mutex mutex_;
128 static inline Stats stats_;
129
130 Stats inc(duration dt)
131 {
132 std::lock_guard lock(mutex_);
133 stats_.total_duration += dt;
134 stats_.count++;
135 if (dt < stats_.min_duration)
136 stats_.min_duration = dt;
137 if (dt > stats_.max_duration)
138 stats_.max_duration = dt;
139 return stats_;
140 }
141};
142
147{
148public:
150 {
151 JAMI_WARNING("WavWriter(): {} ({}, {})",
152 filename,
154 frame->sample_rate);
155 avformat_alloc_output_context2(&format_ctx_, nullptr, "wav", filename);
156 if (!format_ctx_)
157 throw std::runtime_error("Failed to allocate output format context");
158
160 switch (frame->format) {
161 case AV_SAMPLE_FMT_U8:
163 break;
167 break;
171 break;
175 break;
179 break;
182 break;
183 default:
184 throw std::runtime_error("Unsupported audio format");
185 }
186
187 const auto* codec = avcodec_find_encoder(codec_id);
188 if (!codec)
189 throw std::runtime_error("Failed to find audio codec");
190
191 codec_ctx_ = avcodec_alloc_context3(codec);
192 if (!codec_ctx_)
193 throw std::runtime_error("Failed to allocate audio codec context");
194
195 codec_ctx_->sample_fmt = (AVSampleFormat) frame->format;
196 codec_ctx_->ch_layout = frame->ch_layout;
197 codec_ctx_->sample_rate = frame->sample_rate;
198 if (format_ctx_->oformat->flags & AVFMT_GLOBALHEADER)
199 codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
200
201 if (avcodec_open2(codec_ctx_, codec, nullptr) < 0)
202 throw std::runtime_error("Failed to open audio codec");
203
204 stream_ = avformat_new_stream(format_ctx_, codec);
205 if (!stream_)
206 throw std::runtime_error("Failed to create audio stream");
207
208 if (avcodec_parameters_from_context(stream_->codecpar, codec_ctx_) < 0)
209 throw std::runtime_error("Failed to copy codec parameters to stream");
210
211 if (!(format_ctx_->oformat->flags & AVFMT_NOFILE)) {
212 if (avio_open(&format_ctx_->pb, filename, AVIO_FLAG_WRITE) < 0) {
213 throw std::runtime_error("Failed to open output file for writing");
214 }
215 }
216 if (avformat_write_header(format_ctx_, nullptr) < 0)
217 throw std::runtime_error("Failed to write header to output file");
218 }
219
221 {
222 int ret = avcodec_send_frame(codec_ctx_, frame);
223 if (ret < 0)
224 JAMI_ERROR("Error sending a frame to the encoder");
225 while (ret >= 0) {
227 ret = avcodec_receive_packet(codec_ctx_, pkt);
228 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
229 break;
230 else if (ret < 0) {
231 JAMI_ERROR("Error encoding a frame");
232 break;
233 }
234 pkt->stream_index = stream_->index;
235 pkt->pts = lastPts;
236 pkt->dts = lastPts;
237 lastPts += frame->nb_samples * (int64_t) stream_->time_base.den
238 / (stream_->time_base.num * (int64_t) frame->sample_rate);
239 ret = av_write_frame(format_ctx_, pkt);
240 if (ret < 0) {
241 JAMI_ERROR("Error while writing output packet");
242 break;
243 }
245 }
246 }
247
249 {
250 if (codec_ctx_) {
251 avcodec_close(codec_ctx_);
252 avcodec_free_context(&codec_ctx_);
253 }
254 if (format_ctx_) {
255 av_write_trailer(format_ctx_);
256 if (!(format_ctx_->oformat->flags & AVFMT_NOFILE))
257 avio_closep(&format_ctx_->pb);
258 avformat_free_context(format_ctx_);
259 }
260 }
261
262private:
263 AVFormatContext* format_ctx_ {nullptr};
264 AVCodecContext* codec_ctx_ {nullptr};
265 AVStream* stream_ {nullptr};
266 int64_t lastPts {0};
267};
268
273{
274public:
275 VideoWriter(const std::string& filename, AVPixelFormat format, int width, int height)
276 : filename_(filename)
277 , format_(format)
278 , width_(width)
279 , height_(height)
280 {
281 f_ = fopen(filename.c_str(), "wb");
282 }
283
284 // so an int (VideoFrame.format()) can be passed without casting
285 VideoWriter(const std::string& filename, int format, int width, int height)
286 : VideoWriter(filename, static_cast<AVPixelFormat>(format), width, height)
287 {}
288
290 {
291 fclose(f_);
292 JAMI_DBG("Play video file with: ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s",
293 av_get_pix_fmt_name(format_),
294 width_,
295 height_,
296 filename_.c_str());
297 }
298
300 {
301 int ret = 0;
302 uint8_t* buffer = nullptr;
303 auto* f = frame.pointer();
304
305 if (format_ != f->format || width_ != f->width || height_ != f->height)
306 return;
307
308 int size = av_image_get_buffer_size(format_, width_, height_, 1);
309 buffer = reinterpret_cast<uint8_t*>(av_malloc(size));
310 if (!buffer) {
311 return;
312 }
314 size,
315 reinterpret_cast<const uint8_t* const*>(f->data),
316 reinterpret_cast<const int*>(f->linesize),
317 format_,
318 width_,
319 height_,
320 1))
321 < 0) {
322 av_freep((void*) &buffer);
323 return;
324 }
325
326 fwrite(buffer, 1, size, f_);
327 av_freep((void*) &buffer);
328 }
329
330private:
331 FILE* f_;
332 std::string filename_;
333 AVPixelFormat format_;
334 int width_, height_;
335};
336
337} // namespace debug
338} // namespace jami
Ex: Timer t; std::this_thread::sleep_for(std::chrono::milliseconds(10)); JAMI_LOG("Task took {} ns",...
Definition debug_utils.h:49
Timer(std::string_view name)
Definition debug_utils.h:51
void print(std::string_view action) const
Definition debug_utils.h:65
uint64_t getDuration() const
Definition debug_utils.h:59
Minimally invasive video writer.
void write(VideoFrame &frame)
VideoWriter(const std::string &filename, int format, int width, int height)
VideoWriter(const std::string &filename, AVPixelFormat format, int width, int height)
void write(AVFrame *frame)
WavWriter(const char *filename, AVFrame *frame)
std::chrono::steady_clock Clock
Definition debug_utils.h:35
#define JAMI_ERROR(formatstr,...)
Definition logger.h:243
#define JAMI_DBG(...)
Definition logger.h:228
#define JAMI_WARNING(formatstr,...)
Definition logger.h:242
#define JAMI_LOG(formatstr,...)
Definition logger.h:237
void emitSignal(Args... args)
Definition jami_signal.h:64
consteval StringLiteral(const char(&str)[N])
Definition debug_utils.h:78
void av_packet_free(AVPacket **frame)