Loading...
Searching...
No Matches
LibavOutputStream.hpp
1#pragma once
2
3extern "C" {
4
5#include <libavcodec/avcodec.h>
6#include <libavdevice/avdevice.h>
7#include <libavformat/avformat.h>
8#include <libavutil/pixdesc.h>
9#include <libswresample/swresample.h>
10#include <libswscale/swscale.h>
11}
12
13#include <Audio/Settings/Model.hpp>
14#include <Gfx/Libav/AudioFrameEncoder.hpp>
15#include <Gfx/Libav/LibavOutputSettings.hpp>
16
17#include <score/application/ApplicationContext.hpp>
18#include <score/tools/Debug.hpp>
19
20#include <ossia/detail/flat_map.hpp>
21
22#include <QApplication>
23
24#include <CDSPResampler.h>
25
26#include <string>
27
28namespace Gfx
29{
30
32{
33 std::string name;
34 std::string codec;
35 ossia::flat_map<std::string, std::string> options;
36};
37
39{
40 const AVCodec* codec{};
41 AVStream* st{};
42 AVCodecContext* enc{};
43
44 /* pts of the next frame that will be generated */
45 int64_t next_pts{};
46 int samples_count{};
47
48 AVFrame* cache_input_frame{};
49 AVFrame* tmp_frame{};
50
51 AVPacket* tmp_pkt{};
52
53 struct SwsContext* sws_ctx{};
54 std::vector<std::unique_ptr<r8b::CDSPResampler>> resamplers;
55
56 std::unique_ptr<AudioFrameEncoder> encoder;
57
59 const LibavOutputSettings& set, AVFormatContext* oc, const StreamOptions& opts)
60 {
61 codec = avcodec_find_encoder_by_name(opts.codec.c_str());
62 if(!codec)
63 {
64 qDebug() << "Could not find encoder for " << opts.codec.c_str();
65 exit(1);
66 }
67
68 this->tmp_pkt = av_packet_alloc();
69 if(!this->tmp_pkt)
70 {
71 qDebug() << "Could not allocate AVPacket";
72 exit(1);
73 }
74
75 this->st = avformat_new_stream(oc, nullptr);
76 if(!this->st)
77 {
78 qDebug() << "Could not allocate stream";
79 exit(1);
80 }
81 this->st->id = oc->nb_streams - 1;
82
83 // Init hw accel
84 // FIXME IMPLEMENT
85#if 0
86 AVBufferRef* hw_ctx{};
87 {
88 // HW Accel
89 AVHWDeviceType device = AV_HWDEVICE_TYPE_QSV;
90 int ret = av_hwdevice_ctx_create(&hw_ctx, device, "auto", nullptr, 0);
91 if(ret != 0)
92 {
93 qDebug() << "Error while opening hardware encoder: " << av_to_string(ret);
94 exit(1);
95 }
96 }
97#endif
98 this->enc = avcodec_alloc_context3(codec);
99 if(!this->enc)
100 {
101 qDebug() << "Could not alloc an encoding context";
102 exit(1);
103 }
104
105 switch(codec->type)
106 {
107 case AVMEDIA_TYPE_AUDIO:
108 init_audio(set, this->enc);
109 break;
110 case AVMEDIA_TYPE_VIDEO:
111 init_video(set, this->enc);
112 break;
113
114 default:
115 break;
116 }
117
118 /* Some formats want stream headers to be separate. */
119 if(oc->oformat->flags & AVFMT_GLOBALHEADER)
120 {
121 this->enc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
122 }
123 }
124
125 void init_audio(const LibavOutputSettings& set, AVCodecContext* c)
126 {
127#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
128 c->sample_fmt = av_get_sample_fmt(set.audio_converted_smpfmt.toStdString().c_str());
129
130 {
131 const int* supported_samplerates{};
132#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 19, 100)
133 avcodec_get_supported_config(
134 c, codec, AV_CODEC_CONFIG_SAMPLE_RATE, 0, (const void**)&supported_samplerates,
135 nullptr);
136#else
137 supported_samplerates = codec->supported_samplerates;
138#endif
139 if(supported_samplerates)
140 {
141 c->sample_rate = supported_samplerates[0];
142 for(int i = 0; supported_samplerates[i]; i++)
143 {
144 if(supported_samplerates[i] == set.audio_sample_rate)
145 {
146 c->sample_rate = set.audio_sample_rate;
147 break;
148 }
149 }
150 }
151 else
152 {
153 c->sample_rate = set.audio_sample_rate;
154 }
155 }
156
157 c->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;
158 c->ch_layout.nb_channels = set.audio_channels;
159 if(set.audio_encoder_short == "pcm_s24le" || set.audio_encoder_short == "pcm_s24be")
160 c->bits_per_raw_sample = 24;
161
162 this->st->time_base = AVRational{1, c->sample_rate};
163 c->time_base = AVRational{1, c->sample_rate};
164 c->framerate = AVRational{c->sample_rate, 1};
165 qDebug() << "Opening audio encoder with: rate: " << c->sample_rate;
166#endif
167 }
168
169 void init_video(const LibavOutputSettings& set, AVCodecContext* c)
170 {
171 c->codec_id = codec->id;
172 // c->bit_rate = 400000;
173 // c->bit_rate_tolerance = 10000;
174 // c->global_quality = 1;
175 // c->compression_level = 1;
176 // c->hw_device_ctx = hw_ctx;
177
178 // c->flags |= AV_CODEC_FLAG_QSCALE;
179 // c->global_quality = FF_QP2LAMBDA * 3.0;
180 /* Resolution must be a multiple of two. */
181 c->width = set.width;
182 c->height = set.height;
183 /* timebase: This is the fundamental unit of time (in seconds) in terms
184 * of which frame timestamps are represented. For fixed-fps content,
185 * timebase should be 1/framerate and timestamp increments should be
186 * identical to 1. */
187 this->st->time_base = AVRational{100000, int(100000 * set.rate)};
188 c->time_base = this->st->time_base;
189 c->framerate = AVRational{this->st->time_base.den, this->st->time_base.num};
190
191 //c->gop_size = 12; /* emit one intra frame every twelve frames at most */
192
193 // ignored if frame->pict_type is AV_PICTURE_TYPE_I
194 c->gop_size = 0;
195 c->max_b_frames = 0;
196
197 // c->pix_fmt = AV_PIX_FMT_RGB24;
198 c->pix_fmt = av_get_pix_fmt(set.video_converted_pixfmt.toStdString().c_str());
199 c->strict_std_compliance = FF_COMPLIANCE_NORMAL;
200 if(c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
201 {
202 /* just for testing, we also add B-frames */
203 c->max_b_frames = 2;
204 }
205 if(c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
206 {
207 /* Needed to avoid using macroblocks in which some coeffs overflow.
208 * This does not happen with normal video, it just happens here as
209 * the motion of the chroma plane does not match the luma plane. */
210 c->mb_decision = 2;
211 }
212 }
213
214 void open_audio(
215 const LibavOutputSettings& set, AVFormatContext* oc, const AVCodec* codec,
216 AVDictionary* opt_arg)
217 {
218#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
219 AVDictionary* opt = nullptr;
220
221 av_dict_copy(&opt, opt_arg, 0);
222 int ret = avcodec_open2(enc, codec, &opt);
223 av_dict_free(&opt);
224 if(ret < 0)
225 {
226 qDebug() << "Could not open audio codec: " << av_to_string(ret);
227 exit(1);
228 }
229
230 int nb_samples = 0;
231 if(enc->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
232 {
233 auto& audio_stgs = score::AppContext().settings<Audio::Settings::Model>();
234 nb_samples = audio_stgs.getBufferSize();
235 enc->frame_size = nb_samples;
236 qDebug() << "Setting frame_size: " << nb_samples;
237 }
238 else
239 {
240 nb_samples = enc->frame_size;
241 qDebug() << "Forcing frame_size: " << nb_samples;
242 }
243
244 cache_input_frame = alloc_audio_frame(
245 enc->sample_fmt, &enc->ch_layout, enc->sample_rate, nb_samples);
246
247 /* copy the stream parameters to the muxer */
248 ret = avcodec_parameters_from_context(this->st->codecpar, enc);
249 if(ret < 0)
250 {
251 qDebug() << "Could not copy the stream parameters";
252 exit(1);
253 }
254
255 {
256 auto input_fmt = AV_SAMPLE_FMT_FLTP;
257 auto conv_fmt
258 = av_get_sample_fmt(set.audio_converted_smpfmt.toStdString().c_str());
259 SCORE_ASSERT(input_fmt != -1);
260 SCORE_ASSERT(conv_fmt != -1);
261
262 auto& ctx = score::AppContext().settings<Audio::Settings::Model>();
263
264 const int input_sample_rate = ctx.getRate();
265 if(enc->sample_rate != input_sample_rate)
266 {
267 for(int i = 0; i < set.audio_channels; i++)
268 this->resamplers.push_back(std::make_unique<r8b::CDSPResampler>(
269 input_sample_rate, enc->sample_rate, nb_samples * 2, 3.0, 206.91,
270 r8b::fprMinPhase));
271 }
272
273 switch(conv_fmt)
274 {
275 case AV_SAMPLE_FMT_NONE:
276 case AV_SAMPLE_FMT_U8:
277 case AV_SAMPLE_FMT_S16:
278 encoder = std::make_unique<S16IAudioFrameEncoder>(nb_samples);
279 break;
280 case AV_SAMPLE_FMT_S32:
281 if(enc->bits_per_raw_sample == 24)
282 encoder = std::make_unique<S24IAudioFrameEncoder>(nb_samples);
283 else
284 encoder = std::make_unique<S32IAudioFrameEncoder>(nb_samples);
285 break;
286 case AV_SAMPLE_FMT_FLT:
287 encoder = std::make_unique<FltIAudioFrameEncoder>(nb_samples);
288 break;
289 case AV_SAMPLE_FMT_DBL:
290 encoder = std::make_unique<DblIAudioFrameEncoder>(nb_samples);
291 break;
292
293 case AV_SAMPLE_FMT_U8P:
294 case AV_SAMPLE_FMT_S16P:
295 case AV_SAMPLE_FMT_S32P:
296 case AV_SAMPLE_FMT_FLTP:
297 encoder = std::make_unique<FltPAudioFrameEncoder>(nb_samples);
298 break;
299 case AV_SAMPLE_FMT_DBLP:
300 case AV_SAMPLE_FMT_S64:
301 case AV_SAMPLE_FMT_S64P:
302 break;
303 default:
304 break;
305 }
306 }
307#endif
308 }
309
310#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100)
311 static AVFrame* alloc_audio_frame(
312 enum AVSampleFormat sample_fmt, const AVChannelLayout* channel_layout,
313 int sample_rate, int nb_samples)
314 {
315 AVFrame* frame = av_frame_alloc();
316 if(!frame)
317 {
318 qDebug() << "Error allocating an audio frame";
319 exit(1);
320 }
321
322 frame->format = sample_fmt;
323 frame->ch_layout.order = channel_layout->order;
324 frame->ch_layout.nb_channels = channel_layout->nb_channels;
325 frame->sample_rate = sample_rate;
326 frame->nb_samples = nb_samples;
327
328 if(nb_samples)
329 {
330 if(av_frame_get_buffer(frame, 0) < 0)
331 {
332 qDebug() << "Error allocating an audio buffer";
333 exit(1);
334 }
335 }
336
337 return frame;
338 }
339#endif
340
341 static AVFrame* alloc_video_frame(enum AVPixelFormat pix_fmt, int width, int height)
342 {
343 auto frame = av_frame_alloc();
344 if(!frame)
345 return NULL;
346
347 frame->format = pix_fmt;
348 frame->width = width;
349 frame->height = height;
350
351 /* allocate the buffers for the frame data */
352 const int ret = av_frame_get_buffer(frame, 0);
353 if(ret < 0)
354 {
355 qDebug() << "Could not allocate frame data.";
356 exit(1);
357 }
358
359 return frame;
360 }
361
362 void open_video(
363 const LibavOutputSettings& set, AVFormatContext* oc, const AVCodec* codec,
364 AVDictionary* opt_arg)
365 {
366 AVCodecContext* c = this->enc;
367 AVDictionary* opt = nullptr;
368
369 av_dict_copy(&opt, opt_arg, 0);
370
371 /* set some options */
372 int err = av_opt_set_double(this->enc->priv_data, "crf", 0.0, 0);
373 if(err < 0)
374 {
375 qDebug() << "failed to initialize encoder: " << av_to_string(err);
376 }
377
378 /* open the codec */
379 SCORE_ASSERT(this->enc->flags & AV_CODEC_FLAG_GLOBAL_HEADER);
380 int ret = avcodec_open2(this->enc, codec, &opt);
381 av_dict_free(&opt);
382 if(ret < 0)
383 {
384 qDebug() << "Could not open video codec: " << av_to_string(ret);
385 exit(1);
386 }
387
388 /* allocate and init a re-usable frame */
389 this->cache_input_frame = alloc_video_frame(AV_PIX_FMT_RGBA, c->width, c->height);
390 if(!this->cache_input_frame)
391 {
392 qDebug() << "Could not allocate video frame";
393 exit(1);
394 }
395
396 this->tmp_frame = nullptr;
397 // If conversion is needed :
398 // if(c->pix_fmt != AV_PIX_FMT_YUVJ420P)
399 {
400 auto input_fmt = av_get_pix_fmt(set.video_render_pixfmt.toStdString().c_str());
401 auto conv_fmt = av_get_pix_fmt(set.video_converted_pixfmt.toStdString().c_str());
402 SCORE_ASSERT(input_fmt != -1);
403 SCORE_ASSERT(conv_fmt != -1);
404 sws_ctx = sws_getContext(
405 set.width, set.height, input_fmt, set.width, set.height, conv_fmt, 1, nullptr,
406 nullptr, nullptr);
407 SCORE_ASSERT(sws_ctx);
408 this->tmp_frame = alloc_video_frame(conv_fmt, c->width, c->height);
409 if(!this->tmp_frame)
410 {
411 qDebug() << "Could not allocate temporary video frame";
412 exit(1);
413 }
414 }
415
416 /* copy the stream parameters to the muxer */
417 ret = avcodec_parameters_from_context(this->st->codecpar, c);
418 if(ret < 0)
419 {
420 qDebug() << "Could not copy the stream parameters";
421 exit(1);
422 }
423 }
424
425 void open(
426 const LibavOutputSettings& set, AVFormatContext* oc, const AVCodec* codec,
427 AVDictionary* opt_arg)
428 {
429 SCORE_ASSERT(oc);
430 SCORE_ASSERT(codec);
431 SCORE_ASSERT(opt_arg);
432 if(codec->type == AVMEDIA_TYPE_AUDIO)
433 {
434 open_audio(set, oc, codec, opt_arg);
435 }
436 else if(codec->type == AVMEDIA_TYPE_VIDEO)
437 {
438 open_video(set, oc, codec, opt_arg);
439 }
440 }
441
442 void close(AVFormatContext* oc)
443 {
444 avcodec_free_context(&enc);
445 av_frame_free(&cache_input_frame);
446 av_frame_free(&tmp_frame);
447 av_packet_free(&tmp_pkt);
448 sws_freeContext(sws_ctx);
449 sws_ctx = nullptr;
450 }
451
452 AVFrame* get_video_frame()
453 {
454 /* when we pass a frame to the encoder, it may keep a reference to it
455 * internally; make sure we do not overwrite it here */
456 if(av_frame_make_writable(this->cache_input_frame) < 0)
457 exit(1);
458
459 this->cache_input_frame->pts = this->next_pts++;
460
461 return this->cache_input_frame;
462 }
463
464 AVFrame* get_audio_frame()
465 {
466 /* when we pass a frame to the encoder, it may keep a reference to it
467 * internally; make sure we do not overwrite it here */
468 if(av_frame_make_writable(this->cache_input_frame) < 0)
469 exit(1);
470
471 this->cache_input_frame->pts = this->next_pts;
472 this->next_pts += this->enc->frame_size;
473
474 return this->cache_input_frame;
475 }
476
477 int write_video_frame(AVFormatContext* fmt_ctx, AVFrame* input_frame)
478 {
479#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(7, 5, 100)
480 // scale the frame
481 int ret = sws_scale_frame(sws_ctx, tmp_frame, input_frame);
482 if(ret < 0)
483 {
484 qDebug() << "Error during sws_scale_frame: " << av_to_string(ret);
485 exit(1);
486 }
487
488 tmp_frame->quality = FF_LAMBDA_MAX; //c->global_quality;
489 tmp_frame->pict_type = AV_PICTURE_TYPE_I;
490 tmp_frame->pts++;
491
492 // send the frame to the encoder
493 ret = avcodec_send_frame(enc, tmp_frame);
494 if(ret < 0)
495 {
496 qDebug() << "Error sending a frame to the encoder: " << av_to_string(ret);
497 exit(1);
498 }
499
500 while(ret >= 0)
501 {
502 ret = avcodec_receive_packet(enc, tmp_pkt);
503 if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
504 break;
505 else if(ret < 0)
506 {
507 qDebug() << "Error encoding a frame: " << av_to_string(ret);
508 exit(1);
509 }
510
511 /* rescale output packet timestamp values from codec to stream timebase */
512 av_packet_rescale_ts(tmp_pkt, enc->time_base, st->time_base);
513 tmp_pkt->stream_index = st->index;
514 tmp_pkt->flags |= AV_PKT_FLAG_KEY;
515
516 ret = av_interleaved_write_frame(fmt_ctx, tmp_pkt);
517 if(ret < 0)
518 {
519 qDebug() << "Error while writing output packet: " << av_to_string(ret);
520 exit(1);
521 }
522 }
523
524 return ret == AVERROR_EOF ? 1 : 0;
525#endif
526 return 1;
527 }
528
529 // #define SRC_RATE SAMPLE_RATE_TEST
530 // #define DST_RATE SAMPLE_RATE_TEST
531 // static int64_t conv_audio_pts(SwrContext* ctx, int64_t in, int sample_rate)
532 // {
533 // //int64_t d = (int64_t) AUDIO_RATE * AUDIO_RATE;
534 // int64_t d = (int64_t)sample_rate * sample_rate;
535 //
536 // /* Convert from audio_src_tb to 1/(src_samplerate * dst_samplerate) */
537 // in = av_rescale_rnd(in, d, SRC_RATE, AV_ROUND_NEAR_INF);
538 //
539 // /* In units of 1/(src_samplerate * dst_samplerate) */
540 // in = swr_next_pts(ctx, in);
541 //
542 // /* Convert from 1/(src_samplerate * dst_samplerate) to audio_dst_tb */
543 // return av_rescale_rnd(in, DST_RATE, d, AV_ROUND_NEAR_INF);
544 // }
545
546 int write_audio_frame(AVFormatContext* fmt_ctx, AVFrame* input_frame)
547 {
548 // send the frame to the encoder
549 int ret = avcodec_send_frame(enc, input_frame);
550 if(ret < 0)
551 {
552 qDebug() << "Error sending a frame to the encoder: " << av_to_string(ret);
553 exit(1);
554 }
555
556 while(ret >= 0)
557 {
558 ret = avcodec_receive_packet(enc, tmp_pkt);
559 if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
560 break;
561 else if(ret < 0)
562 {
563 qDebug() << "Error encoding a frame: " << av_to_string(ret);
564 exit(1);
565 }
566
567 /* rescale output packet timestamp values from codec to stream timebase */
568 av_packet_rescale_ts(tmp_pkt, enc->time_base, st->time_base);
569 tmp_pkt->stream_index = st->index;
570
571 ret = av_interleaved_write_frame(fmt_ctx, tmp_pkt);
572 if(ret < 0)
573 {
574 qDebug() << "Error while writing output packet: " << av_to_string(ret);
575 exit(1);
576 }
577 }
578
579 return ret == AVERROR_EOF ? 1 : 0;
580 }
581};
582}
Definition score-plugin-audio/Audio/Settings/Model.hpp:22
Binds the rendering pipeline to ossia processes.
Definition CameraDevice.cpp:28
Definition LibavOutputSettings.hpp:16
Definition LibavOutputStream.hpp:39
Definition LibavOutputStream.hpp:32
T & settings() const
Access a specific Settings model instance.
Definition ApplicationContext.hpp:40