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_DBG() << name_ << ": " << action << " after " << dht::print_duration(Clock::now() - start_);
66 }
67
68private:
69 std::string_view name_;
70 std::chrono::time_point<Clock> start_;
71};
72
76class WavWriter {
77public:
78 WavWriter(const char* filename, AVFrame* frame)
79 {
80 JAMI_WARNING("WavWriter(): {} ({}, {})", filename, av_get_sample_fmt_name((AVSampleFormat)frame->format), frame->sample_rate);
81 avformat_alloc_output_context2(&format_ctx_, nullptr, "wav", filename);
82 if (!format_ctx_)
83 throw std::runtime_error("Failed to allocate output format context");
84
86 switch (frame->format) {
89 break;
93 break;
97 break;
101 break;
105 break;
108 break;
109 default:
110 throw std::runtime_error("Unsupported audio format");
111 }
112
113 auto codec = avcodec_find_encoder(codec_id);
114 if (!codec)
115 throw std::runtime_error("Failed to find audio codec");
116
117 codec_ctx_ = avcodec_alloc_context3(codec);
118 if (!codec_ctx_)
119 throw std::runtime_error("Failed to allocate audio codec context");
120
121 codec_ctx_->sample_fmt = (AVSampleFormat)frame->format;
122 codec_ctx_->ch_layout = frame->ch_layout;
123 codec_ctx_->sample_rate = frame->sample_rate;
124 if (format_ctx_->oformat->flags & AVFMT_GLOBALHEADER)
125 codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
126
127 if (avcodec_open2(codec_ctx_, codec, nullptr) < 0)
128 throw std::runtime_error("Failed to open audio codec");
129
130 stream_ = avformat_new_stream(format_ctx_, codec);
131 if (!stream_)
132 throw std::runtime_error("Failed to create audio stream");
133
134 if (avcodec_parameters_from_context(stream_->codecpar, codec_ctx_) < 0)
135 throw std::runtime_error("Failed to copy codec parameters to stream");
136
137 if (!(format_ctx_->oformat->flags & AVFMT_NOFILE)) {
138 if (avio_open(&format_ctx_->pb, filename, AVIO_FLAG_WRITE) < 0) {
139 throw std::runtime_error("Failed to open output file for writing");
140 }
141 }
142 if (avformat_write_header(format_ctx_, nullptr) < 0)
143 throw std::runtime_error("Failed to write header to output file");
144 }
145
147 int ret = avcodec_send_frame(codec_ctx_, frame);
148 if (ret < 0)
149 JAMI_ERROR("Error sending a frame to the encoder");
150 while (ret >= 0) {
152 ret = avcodec_receive_packet(codec_ctx_, pkt);
153 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
154 break;
155 else if (ret < 0) {
156 JAMI_ERROR("Error encoding a frame");
157 break;
158 }
159 pkt->stream_index = stream_->index;
160 pkt->pts = lastPts;
161 pkt->dts = lastPts;
162 lastPts += frame->nb_samples * (int64_t)stream_->time_base.den / (stream_->time_base.num * (int64_t)frame->sample_rate);
163 ret = av_write_frame(format_ctx_, pkt);
164 if (ret < 0) {
165 JAMI_ERROR("Error while writing output packet");
166 break;
167 }
169 }
170 }
171
173 if (codec_ctx_) {
174 avcodec_close(codec_ctx_);
175 avcodec_free_context(&codec_ctx_);
176 }
177 if (format_ctx_) {
178 av_write_trailer(format_ctx_);
179 if (!(format_ctx_->oformat->flags & AVFMT_NOFILE))
180 avio_closep(&format_ctx_->pb);
181 avformat_free_context(format_ctx_);
182 }
183 }
184private:
185 AVFormatContext* format_ctx_ {nullptr};
186 AVCodecContext* codec_ctx_ {nullptr};
187 AVStream* stream_ {nullptr};
188 int64_t lastPts {0};
189};
190
195{
196public:
197 VideoWriter(const std::string& filename, AVPixelFormat format, int width, int height)
198 : filename_(filename)
199 , format_(format)
200 , width_(width)
201 , height_(height)
202 {
203 f_ = fopen(filename.c_str(), "wb");
204 }
205
206 // so an int (VideoFrame.format()) can be passed without casting
207 VideoWriter(const std::string& filename, int format, int width, int height)
208 : VideoWriter(filename, static_cast<AVPixelFormat>(format), width, height)
209 {}
210
212 {
213 fclose(f_);
214 JAMI_DBG("Play video file with: ffplay -f rawvideo -pixel_format %s -video_size %dx%d %s",
215 av_get_pix_fmt_name(format_),
216 width_,
217 height_,
218 filename_.c_str());
219 }
220
222 {
223 int ret = 0;
224 uint8_t* buffer = nullptr;
225 auto f = frame.pointer();
226
227 if (format_ != f->format || width_ != f->width || height_ != f->height)
228 return;
229
230 int size = av_image_get_buffer_size(format_, width_, height_, 1);
231 buffer = reinterpret_cast<uint8_t*>(av_malloc(size));
232 if (!buffer) {
233 return;
234 }
236 size,
237 reinterpret_cast<const uint8_t* const*>(f->data),
238 reinterpret_cast<const int*>(f->linesize),
239 format_,
240 width_,
241 height_,
242 1))
243 < 0) {
245 return;
246 }
247
248 fwrite(buffer, 1, size, f_);
250 }
251
252private:
253 FILE* f_;
254 std::string filename_;
255 AVPixelFormat format_;
256 int width_, height_;
257};
258
259} // namespace debug
260} // 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)
Definition debug_utils.h:78
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
void emitSignal(Args... args)
Definition ring_signal.h:64
void av_packet_free(AVPacket **frame)