Ring Daemon 16.0.0
Loading...
Searching...
No Matches
debug_utils.h
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#pragma once
18
19#include "libav_deps.h"
20#include "media_io_handle.h"
22
23#include <opendht/utils.h>
24
25#include <chrono>
26#include <cstdio>
27#include <fstream>
28#include <ios>
29#include <ratio>
30#include <string_view>
31
32#include "logger.h"
33
34#warning Debug utilities included in build
35
36using Clock = std::chrono::steady_clock;
37using namespace std::literals;
38
39namespace jami {
40namespace debug {
41
48class Timer
49{
50public:
51 Timer(std::string_view name) : name_(name), start_(Clock::now()) {}
52
54 print("end"sv);
55 }
56
57 template<class Period = std::ratio<1>>
59 {
60 auto diff = std::chrono::duration_cast<Period>(Clock::now() - start_);
61 return diff.count();
62 }
63
64 void print(std::string_view action) const {
65 JAMI_LOG("{}: {} after {}", name_, action, dht::print_duration(Clock::now() - start_));
66 }
67
68private:
69 std::string_view name_;
70 std::chrono::time_point<Clock> start_;
71};
72
73/*
74 * Ex:
75 * STATS_TIMER(TaskName);
76 * std::this_thread::sleep_for(std::chrono::milliseconds(10));
77 * // Timer automatically prints stats on destruction
78 */
79template<typename Tag>
81{
82 using time_point = std::chrono::time_point<Clock>;
83 using duration = Clock::duration;
84public:
85 StatsTimer() : start_(Clock::now()) {}
86
88 auto duration = Clock::now() - start_;
89 auto [avg, count] = inc(duration);
90 JAMI_LOG("{}: end after {}", Tag::name(), dht::print_duration(duration));
91 if (count > 1) {
92 JAMI_LOG("{}: Average duration: {} ({})", Tag::name(), dht::print_duration(avg), count);
93 }
94 }
95private:
96 time_point start_;
97 static inline std::mutex mutex_;
98 static inline duration total_duration_ {};
99 static inline duration::rep count_ {0};
100
101 std::pair<duration, duration::rep> inc(duration dt) {
102 std::lock_guard lock(mutex_);
103 total_duration_ += dt;
104 count_++;
105 return {total_duration_ / count_, count_};
106 }
107};
108
109#define STATS_TIMER(tag) \
110 struct StatsTimerTag_##tag { \
111 static constexpr std::string_view name() { return #tag; } \
112 }; \
113 jami::debug::StatsTimer<StatsTimerTag_##tag> stats_timer_##tag
114
119public:
120 WavWriter(const char* filename, AVFrame* frame)
121 {
122 JAMI_WARNING("WavWriter(): {} ({}, {})", filename, av_get_sample_fmt_name((AVSampleFormat)frame->format), frame->sample_rate);
123 avformat_alloc_output_context2(&format_ctx_, nullptr, "wav", filename);
124 if (!format_ctx_)
125 throw std::runtime_error("Failed to allocate output format context");
126
128 switch (frame->format) {
129 case AV_SAMPLE_FMT_U8:
131 break;
135 break;
139 break;
143 break;
147 break;
150 break;
151 default:
152 throw std::runtime_error("Unsupported audio format");
153 }
154
155 auto codec = avcodec_find_encoder(codec_id);
156 if (!codec)
157 throw std::runtime_error("Failed to find audio codec");
158
159 codec_ctx_ = avcodec_alloc_context3(codec);
160 if (!codec_ctx_)
161 throw std::runtime_error("Failed to allocate audio codec context");
162
163 codec_ctx_->sample_fmt = (AVSampleFormat)frame->format;
164 codec_ctx_->ch_layout = frame->ch_layout;
165 codec_ctx_->sample_rate = frame->sample_rate;
166 if (format_ctx_->oformat->flags & AVFMT_GLOBALHEADER)
167 codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
168
169 if (avcodec_open2(codec_ctx_, codec, nullptr) < 0)
170 throw std::runtime_error("Failed to open audio codec");
171
172 stream_ = avformat_new_stream(format_ctx_, codec);
173 if (!stream_)
174 throw std::runtime_error("Failed to create audio stream");
175
176 if (avcodec_parameters_from_context(stream_->codecpar, codec_ctx_) < 0)
177 throw std::runtime_error("Failed to copy codec parameters to stream");
178
179 if (!(format_ctx_->oformat->flags & AVFMT_NOFILE)) {
180 if (avio_open(&format_ctx_->pb, filename, AVIO_FLAG_WRITE) < 0) {
181 throw std::runtime_error("Failed to open output file for writing");
182 }
183 }
184 if (avformat_write_header(format_ctx_, nullptr) < 0)
185 throw std::runtime_error("Failed to write header to output file");
186 }
187
189 int ret = avcodec_send_frame(codec_ctx_, frame);
190 if (ret < 0)
191 JAMI_ERROR("Error sending a frame to the encoder");
192 while (ret >= 0) {
194 ret = avcodec_receive_packet(codec_ctx_, pkt);
195 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
196 break;
197 else if (ret < 0) {
198 JAMI_ERROR("Error encoding a frame");
199 break;
200 }
201 pkt->stream_index = stream_->index;
202 pkt->pts = lastPts;
203 pkt->dts = lastPts;
204 lastPts += frame->nb_samples * (int64_t)stream_->time_base.den / (stream_->time_base.num * (int64_t)frame->sample_rate);
205 ret = av_write_frame(format_ctx_, pkt);
206 if (ret < 0) {
207 JAMI_ERROR("Error while writing output packet");
208 break;
209 }
211 }
212 }
213
215 if (codec_ctx_) {
216 avcodec_close(codec_ctx_);
217 avcodec_free_context(&codec_ctx_);
218 }
219 if (format_ctx_) {
220 av_write_trailer(format_ctx_);
221 if (!(format_ctx_->oformat->flags & AVFMT_NOFILE))
222 avio_closep(&format_ctx_->pb);
223 avformat_free_context(format_ctx_);
224 }
225 }
226private:
227 AVFormatContext* format_ctx_ {nullptr};
228 AVCodecContext* codec_ctx_ {nullptr};
229 AVStream* stream_ {nullptr};
230 int64_t lastPts {0};
231};
232
237{
238public:
239 VideoWriter(const std::string& filename, AVPixelFormat format, int width, int height)
240 : filename_(filename)
241 , format_(format)
242 , width_(width)
243 , height_(height)
244 {
245 f_ = fopen(filename.c_str(), "wb");
246 }
247
248 // so an int (VideoFrame.format()) can be passed without casting
249 VideoWriter(const std::string& filename, int format, int width, int height)
250 : VideoWriter(filename, static_cast<AVPixelFormat>(format), width, height)
251 {}
252
254 {
255 fclose(f_);
256 JAMI_DBG("Play video file with: ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s",
257 av_get_pix_fmt_name(format_),
258 width_,
259 height_,
260 filename_.c_str());
261 }
262
264 {
265 int ret = 0;
266 uint8_t* buffer = nullptr;
267 auto f = frame.pointer();
268
269 if (format_ != f->format || width_ != f->width || height_ != f->height)
270 return;
271
272 int size = av_image_get_buffer_size(format_, width_, height_, 1);
273 buffer = reinterpret_cast<uint8_t*>(av_malloc(size));
274 if (!buffer) {
275 return;
276 }
278 size,
279 reinterpret_cast<const uint8_t* const*>(f->data),
280 reinterpret_cast<const int*>(f->linesize),
281 format_,
282 width_,
283 height_,
284 1))
285 < 0) {
287 return;
288 }
289
290 fwrite(buffer, 1, size, f_);
292 }
293
294private:
295 FILE* f_;
296 std::string filename_;
297 AVPixelFormat format_;
298 int width_, height_;
299};
300
301} // namespace debug
302} // namespace jami
Ex: Timer t; std::this_thread::sleep_for(std::chrono::milliseconds(10)); JAMI_DBG() << "Task took " <...
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:64
uint64_t getDuration() const
Definition debug_utils.h:58
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:36
#define JAMI_ERROR(formatstr,...)
Definition logger.h:228
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARNING(formatstr,...)
Definition logger.h:227
#define JAMI_LOG(formatstr,...)
Definition logger.h:225
void emitSignal(Args... args)
Definition ring_signal.h:64
void av_packet_free(AVPacket **frame)