MXVK Vulkan Framework 0.33.1
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxwrite.cpp File Reference
#include "mxwrite.hpp"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/mastering_display_metadata.h>
#include <libavutil/mathematics.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libswscale/swscale.h>

Go to the source code of this file.

Classes

struct  anonymous_namespace{mxwrite.cpp}::FfmpegOption

Namespaces

namespace  anonymous_namespace{mxwrite.cpp}

Functions

void anonymous_namespace{mxwrite.cpp}::append_encoder_options (void *object, std::vector< EncoderOptionInfo > &result, std::set< std::string > &seen)
bool anonymous_namespace{mxwrite.cpp}::apply_ffmpeg_options (const std::vector< FfmpegOption > &options, AVCodecContext *context, AVFormatContext *output_context)
std::vector< EncoderInfoavailable_video_encoders ()
AVPixelFormat anonymous_namespace{mxwrite.cpp}::choose_software_pixel_format (const AVCodec *codec, AVPixelFormat requested_format)
uint16_t anonymous_namespace{mxwrite.cpp}::clamp10 (float v)
void cleanup_contexts (AVFormatContext *source_ctx, AVFormatContext *dest_ctx, AVFormatContext *output_ctx)
 Free FFmpeg format contexts used during transfer operations.
bool anonymous_namespace{mxwrite.cpp}::codec_uses_hardware (const AVCodec *codec)
void anonymous_namespace{mxwrite.cpp}::convertBt2020Rgba16EncodedToYuv420p10 (const uint16_t *rgba, int src_stride_shorts, uint16_t *y_plane, int y_stride_shorts, uint16_t *u_plane, int u_stride_shorts, uint16_t *v_plane, int v_stride_shorts, int width, int height)
void anonymous_namespace{mxwrite.cpp}::convertRgbaToBt2020PqYuv420p10 (const uint8_t *rgba, int src_stride_bytes, uint16_t *y_plane, int y_stride_shorts, uint16_t *u_plane, int u_stride_shorts, uint16_t *v_plane, int v_stride_shorts, int width, int height)
const std::string * anonymous_namespace{mxwrite.cpp}::find_ffmpeg_option (const std::vector< FfmpegOption > &options, const std::string &name)
bool anonymous_namespace{mxwrite.cpp}::is_codec_name (const std::string &value)
bool is_format_supported (const char *filename)
bool anonymous_namespace{mxwrite.cpp}::is_hardware_pixel_format (AVPixelFormat format)
bool anonymous_namespace{mxwrite.cpp}::is_nvenc_tune (const std::string &tune)
bool anonymous_namespace{mxwrite.cpp}::is_reserved_ffmpeg_option (const std::string &name)
bool anonymous_namespace{mxwrite.cpp}::is_valid_x264_preset (const std::string &p)
bool anonymous_namespace{mxwrite.cpp}::looks_like_option (const std::string &token)
std::string anonymous_namespace{mxwrite.cpp}::lowercase_ascii (std::string text)
std::string anonymous_namespace{mxwrite.cpp}::normalize_ffmpeg_option_name (std::string name)
std::string anonymous_namespace{mxwrite.cpp}::number_text (double value)
std::string anonymous_namespace{mxwrite.cpp}::nvenc_preset_to_software (const std::string &preset)
int anonymous_namespace{mxwrite.cpp}::option_base_type (AVOptionType type)
bool anonymous_namespace{mxwrite.cpp}::option_has_numeric_range (AVOptionType type)
std::string anonymous_namespace{mxwrite.cpp}::option_type_name (AVOptionType type)
bool anonymous_namespace{mxwrite.cpp}::parse_ffmpeg_options (const std::string &text, std::vector< FfmpegOption > &options)
float anonymous_namespace{mxwrite.cpp}::pqOetf (float L)
bool anonymous_namespace{mxwrite.cpp}::set_named_encoder_option (void *object, const char *name, const std::string &value)
std::vector< FfmpegOptionanonymous_namespace{mxwrite.cpp}::software_fallback_options (const std::vector< FfmpegOption > &options, bool use_hevc_codec)
float anonymous_namespace{mxwrite.cpp}::srgbEotf (float v)
std::vector< AVPixelFormat > anonymous_namespace{mxwrite.cpp}::supported_pixel_formats (const AVCodec *codec)
bool anonymous_namespace{mxwrite.cpp}::tokenize_ffmpeg_options (const std::string &text, std::vector< std::string > &tokens, std::string &error)
void transfer_audio (std::string_view sourceAudioFile, std::string_view destVideoFile)
 Copy audio from one video file to another.
std::vector< EncoderOptionInfovideo_encoder_options (std::string_view encoder_name)
 Return the options exposed by one registered video encoder.
const char * anonymous_namespace{mxwrite.cpp}::x264_preset_to_nvenc (const std::string &p)

Variables

constexpr float anonymous_namespace{mxwrite.cpp}::kPqC1 = 3424.0f / 4096.0f
constexpr float anonymous_namespace{mxwrite.cpp}::kPqC2 = (2413.0f / 4096.0f) * 32.0f
constexpr float anonymous_namespace{mxwrite.cpp}::kPqC3 = (2392.0f / 4096.0f) * 32.0f
constexpr float anonymous_namespace{mxwrite.cpp}::kPqM1 = 2610.0f / 16384.0f
constexpr float anonymous_namespace{mxwrite.cpp}::kPqM2 = (2523.0f / 4096.0f) * 128.0f
constexpr float anonymous_namespace{mxwrite.cpp}::kSdrRefFraction = 100.0f / 10000.0f
std::mutex transfer_audio_mutex

Function Documentation

◆ available_video_encoders()

std::vector< EncoderInfo > available_video_encoders ( )
Returns
Video encoders registered by the linked FFmpeg libraries.

Definition at line 980 of file mxwrite.cpp.

980 {
981 std::vector<EncoderInfo> result;
982 void *iterator = nullptr;
983 while (const AVCodec *codec = av_codec_iterate(&iterator)) {
984 if (!av_codec_is_encoder(codec) || codec->type != AVMEDIA_TYPE_VIDEO || !codec->name) {
985 continue;
986 }
987 EncoderInfo info;
988 info.name = codec->name;
989 info.long_name = codec->long_name ? codec->long_name : codec->name;
990 info.codec_name = avcodec_get_name(codec->id);
991 info.hardware = codec_uses_hardware(codec);
992 info.experimental = (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) != 0;
993 const std::vector<AVPixelFormat> formats = supported_pixel_formats(codec);
994 for (AVPixelFormat format : formats) {
995 const char *name = av_get_pix_fmt_name(format);
996 if (!name) {
997 continue;
998 }
999 if (!info.pixel_formats.empty()) {
1000 info.pixel_formats += ", ";
1001 }
1002 info.pixel_formats += name;
1003 }
1004 result.push_back(std::move(info));
1005 }
1006 std::sort(result.begin(), result.end(), [](const EncoderInfo &left, const EncoderInfo &right) {
1007 if (left.codec_name != right.codec_name) {
1008 return left.codec_name < right.codec_name;
1009 }
1010 if (left.hardware != right.hardware) {
1011 return left.hardware > right.hardware;
1012 }
1013 return left.name < right.name;
1014 });
1015 return result;
1016}
std::vector< AVPixelFormat > supported_pixel_formats(const AVCodec *codec)
Definition mxwrite.cpp:770
bool codec_uses_hardware(const AVCodec *codec)
Definition mxwrite.cpp:840
A video encoder reported by the linked FFmpeg installation.
Definition mxwrite.hpp:39
std::string long_name
Human-readable encoder description.
Definition mxwrite.hpp:41
std::string pixel_formats
Comma-separated supported input pixel formats.
Definition mxwrite.hpp:43
std::string name
Exact libavcodec encoder name (for example, libx265).
Definition mxwrite.hpp:40
std::string codec_name
Encoded format name (for example, hevc or av1).
Definition mxwrite.hpp:42
bool experimental
True when FFmpeg marks the encoder experimental.
Definition mxwrite.hpp:45
bool hardware
True for hardware or hybrid encoders.
Definition mxwrite.hpp:44

◆ cleanup_contexts()

void cleanup_contexts ( AVFormatContext * source_ctx,
AVFormatContext * dest_ctx,
AVFormatContext * output_ctx )

Free FFmpeg format contexts used during transfer operations.

Parameters
source_ctxSource format context.
dest_ctxDestination format context.
output_ctxOutput format context.

Definition at line 226 of file mxwrite.cpp.

228 {
229 if (source_ctx)
230 avformat_close_input(&source_ctx);
231 if (dest_ctx)
232 avformat_close_input(&dest_ctx);
233 if (output_ctx) {
234 if (!(output_ctx->oformat->flags & AVFMT_NOFILE))
235 avio_closep(&output_ctx->pb);
236 avformat_free_context(output_ctx);
237 }
238}

◆ is_format_supported()

bool is_format_supported ( const char * filename)

Definition at line 206 of file mxwrite.cpp.

206 {
207 const char *ext = strrchr(filename, '.');
208 if (!ext)
209 return false;
210 // Lowercase the extension for case-insensitive comparison.
211 std::string lower_ext(ext);
212 std::transform(lower_ext.begin(), lower_ext.end(), lower_ext.begin(),
213 [](unsigned char c) { return std::tolower(c); });
214 static const char *kSupported[] = {
215 ".mp4", ".mkv", ".mov", ".avi", ".m4v",
216 ".ts", ".mts", ".m2ts", ".mpg", ".mpeg",
217 ".flv", ".f4v", ".3gp", ".3g2", ".wmv",
218 ".asf", ".vob"};
219 for (const char *s : kSupported) {
220 if (lower_ext == s)
221 return true;
222 }
223 return false;
224}

◆ transfer_audio()

void transfer_audio ( std::string_view sourceAudioFile,
std::string_view destVideoFile )

Copy audio from one video file to another.

Parameters
sourceAudioFileInput media file containing the audio stream.
destVideoFileOutput video file to receive the audio stream.

Definition at line 240 of file mxwrite.cpp.

240 {
241 std::lock_guard<std::mutex> lock(transfer_audio_mutex);
242 if (!is_format_supported(destVideoFile.data())) {
243 std::cerr << "Unsupported output format. Supported formats: .mp4, .mkv, .avi, .mov\n";
244 return;
245 }
246
247 AVFormatContext *source_ctx = nullptr, *dest_ctx = nullptr, *output_ctx = nullptr;
248 int source_audio_idx = -1, dest_video_idx = -1, dest_audio_idx = -1;
249 std::string temp_output = std::string(destVideoFile) + ".tmp";
250
251 if (avformat_open_input(&source_ctx, sourceAudioFile.data(), nullptr, nullptr) != 0 ||
252 avformat_open_input(&dest_ctx, destVideoFile.data(), nullptr, nullptr) != 0) {
253 std::cerr << "Failed to open input files\n";
254 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
255 return;
256 }
257
258 if (avformat_find_stream_info(source_ctx, nullptr) < 0 ||
259 avformat_find_stream_info(dest_ctx, nullptr) < 0) {
260 std::cerr << "Failed to find stream info\n";
261 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
262 return;
263 }
264
265 for (unsigned i = 0; i < source_ctx->nb_streams; ++i) {
266 if (source_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
267 source_audio_idx = i;
268 break;
269 }
270 }
271
272 for (unsigned i = 0; i < dest_ctx->nb_streams; ++i) {
273 AVMediaType type = dest_ctx->streams[i]->codecpar->codec_type;
274 if (type == AVMEDIA_TYPE_VIDEO)
275 dest_video_idx = i;
276 else if (type == AVMEDIA_TYPE_AUDIO)
277 dest_audio_idx = i;
278 }
279
280 if (source_audio_idx == -1 || dest_video_idx == -1) {
281 std::cerr << "Required streams not found\n";
282 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
283 return;
284 }
285
286 const AVOutputFormat *output_fmt = av_guess_format(nullptr, destVideoFile.data(), nullptr);
287 if (!output_fmt) {
288 output_fmt = av_guess_format("mp4", nullptr, nullptr);
289 if (!output_fmt) {
290 std::cerr << "Failed to determine output format\n";
291 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
292 return;
293 }
294 }
295
296 if (avformat_alloc_output_context2(&output_ctx, output_fmt, nullptr, temp_output.c_str()) < 0) {
297 std::cerr << "Failed to create output context\n";
298 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
299 return;
300 }
301
302 const AVCodec *audio_codec = avcodec_find_decoder(source_ctx->streams[source_audio_idx]->codecpar->codec_id);
303 if (!audio_codec) {
304 std::cerr << "Failed to find audio decoder\n";
305 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
306 return;
307 }
308
309 for (unsigned i = 0; i < dest_ctx->nb_streams; ++i) {
310 if (dest_ctx->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
311 continue;
312 }
313
314 AVStream *dest_stream = dest_ctx->streams[i];
315 AVStream *out_stream = avformat_new_stream(output_ctx, nullptr);
316 if (!out_stream) {
317 std::cerr << "Failed to create output stream\n";
318 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
319 return;
320 }
321
322 if (avcodec_parameters_copy(out_stream->codecpar, dest_stream->codecpar) < 0) {
323 std::cerr << "Failed to copy video parameters\n";
324 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
325 return;
326 }
327
328 out_stream->time_base = dest_stream->time_base;
329 out_stream->codecpar->codec_tag = 0;
330 }
331
332 AVStream *out_stream = avformat_new_stream(output_ctx, audio_codec);
333 if (!out_stream) {
334 std::cerr << "Failed to create audio stream\n";
335 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
336 return;
337 }
338
339 AVCodecParameters *source_params = source_ctx->streams[source_audio_idx]->codecpar;
340 if (avcodec_parameters_copy(out_stream->codecpar, source_params) < 0) {
341 std::cerr << "Failed to copy audio parameters\n";
342 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
343 return;
344 }
345
346 if (source_params->frame_size == 0) {
347 out_stream->codecpar->frame_size = 1024;
348 } else {
349 out_stream->codecpar->frame_size = source_params->frame_size;
350 }
351
352 out_stream->time_base = source_ctx->streams[source_audio_idx]->time_base;
353 out_stream->codecpar->codec_tag = 0;
354 dest_audio_idx = out_stream->index;
355
356 if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {
357 if (avio_open(&output_ctx->pb, temp_output.c_str(), AVIO_FLAG_WRITE) < 0) {
358 std::cerr << "Failed to open output file\n";
359 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
360 return;
361 }
362 }
363 if (avformat_write_header(output_ctx, nullptr) < 0) {
364 std::cerr << "Failed to write header\n";
365 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
366 return;
367 }
368 AVPacket packet;
369 while (av_read_frame(dest_ctx, &packet) >= 0) {
370 if (packet.stream_index == dest_audio_idx) {
371 av_packet_unref(&packet);
372 continue;
373 }
374
375 AVStream *in_stream = dest_ctx->streams[packet.stream_index];
376 AVStream *out_stream = output_ctx->streams[packet.stream_index];
377 av_packet_rescale_ts(&packet, in_stream->time_base, out_stream->time_base);
378
379 if (av_interleaved_write_frame(output_ctx, &packet) < 0) {
380 std::cerr << "Failed to write packet\n";
381 av_packet_unref(&packet);
382 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
383 return;
384 }
385 av_packet_unref(&packet);
386 }
387
388 int64_t video_duration_ts = 0;
389 {
390 AVStream *vid_stream = dest_ctx->streams[0];
391 if (vid_stream->duration > 0) {
392 video_duration_ts = av_rescale_q(vid_stream->duration, vid_stream->time_base, source_ctx->streams[source_audio_idx]->time_base);
393 } else if (dest_ctx->duration > 0) {
394 AVRational av_tb = {1, AV_TIME_BASE};
395 video_duration_ts = av_rescale_q(dest_ctx->duration, av_tb, source_ctx->streams[source_audio_idx]->time_base);
396 }
397 }
398
399 av_seek_frame(source_ctx, source_audio_idx, 0, AVSEEK_FLAG_BACKWARD);
400 while (av_read_frame(source_ctx, &packet) >= 0) {
401 if (packet.stream_index == source_audio_idx) {
402 if (video_duration_ts > 0 && packet.pts != AV_NOPTS_VALUE && packet.pts > video_duration_ts) {
403 av_packet_unref(&packet);
404 break;
405 }
406 AVStream *in_stream = source_ctx->streams[packet.stream_index];
407 AVStream *out_stream = output_ctx->streams[dest_audio_idx];
408 av_packet_rescale_ts(&packet, in_stream->time_base, out_stream->time_base);
409 packet.stream_index = dest_audio_idx;
410
411 if (av_interleaved_write_frame(output_ctx, &packet) < 0) {
412 std::cerr << "Failed to write audio packet\n";
413 av_packet_unref(&packet);
414 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
415 return;
416 }
417 }
418 av_packet_unref(&packet);
419 }
420 av_write_trailer(output_ctx);
421 cleanup_contexts(source_ctx, dest_ctx, output_ctx);
422 std::remove(destVideoFile.data());
423 std::rename(temp_output.c_str(), destVideoFile.data());
424}
std::mutex transfer_audio_mutex
Definition mxwrite.cpp:204
bool is_format_supported(const char *filename)
Definition mxwrite.cpp:206
void cleanup_contexts(AVFormatContext *source_ctx, AVFormatContext *dest_ctx, AVFormatContext *output_ctx)
Free FFmpeg format contexts used during transfer operations.
Definition mxwrite.cpp:226

◆ video_encoder_options()

std::vector< EncoderOptionInfo > video_encoder_options ( std::string_view encoder_name)

Return the options exposed by one registered video encoder.

Parameters
encoder_nameExact encoder name returned by available_video_encoders().

Definition at line 1018 of file mxwrite.cpp.

1018 {
1019 const std::string name(encoder_name);
1020 const AVCodec *codec = avcodec_find_encoder_by_name(name.c_str());
1021 if (!codec || codec->type != AVMEDIA_TYPE_VIDEO) {
1022 return {};
1023 }
1024 AVCodecContext *context = avcodec_alloc_context3(codec);
1025 if (!context) {
1026 return {};
1027 }
1028 std::vector<EncoderOptionInfo> result;
1029 std::set<std::string> seen;
1030 append_encoder_options(context->priv_data, result, seen);
1031 append_encoder_options(context, result, seen);
1032 avcodec_free_context(&context);
1033 std::sort(result.begin(), result.end(), [](const EncoderOptionInfo &left, const EncoderOptionInfo &right) {
1034 return left.name < right.name;
1035 });
1036 return result;
1037}
void append_encoder_options(void *object, std::vector< EncoderOptionInfo > &result, std::set< std::string > &seen)
Definition mxwrite.cpp:930
One configurable AVOption exposed by a video encoder.
Definition mxwrite.hpp:49

Variable Documentation

◆ transfer_audio_mutex

std::mutex transfer_audio_mutex

Definition at line 204 of file mxwrite.cpp.