MXVK Vulkan Framework 0.35.0
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< EncoderInfo > available_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< FfmpegOption > anonymous_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< EncoderOptionInfo > video_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 920 of file mxwrite.cpp.

920 {
921 std::vector<EncoderInfo> result;
922 void *iterator = nullptr;
923 while (const AVCodec *codec = av_codec_iterate(&iterator)) {
924 if (!av_codec_is_encoder(codec) || codec->type != AVMEDIA_TYPE_VIDEO || !codec->name) {
925 continue;
926 }
927 EncoderInfo info;
928 info.name = codec->name;
929 info.long_name = codec->long_name ? codec->long_name : codec->name;
930 info.codec_name = avcodec_get_name(codec->id);
931 info.hardware = codec_uses_hardware(codec);
932 info.experimental = (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) != 0;
933 const std::vector<AVPixelFormat> formats = supported_pixel_formats(codec);
934 for (AVPixelFormat format : formats) {
935 const char *name = av_get_pix_fmt_name(format);
936 if (!name) {
937 continue;
938 }
939 if (!info.pixel_formats.empty()) {
940 info.pixel_formats += ", ";
941 }
942 info.pixel_formats += name;
943 }
944 result.push_back(std::move(info));
945 }
946 std::sort(result.begin(), result.end(), [](const EncoderInfo &left, const EncoderInfo &right) {
947 if (left.codec_name != right.codec_name) {
948 return left.codec_name < right.codec_name;
949 }
950 if (left.hardware != right.hardware) {
951 return left.hardware > right.hardware;
952 }
953 return left.name < right.name;
954 });
955 return result;
956}
std::vector< AVPixelFormat > supported_pixel_formats(const AVCodec *codec)
Definition mxwrite.cpp:735
bool codec_uses_hardware(const AVCodec *codec)
Definition mxwrite.cpp:794
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 209 of file mxwrite.cpp.

209 {
210 if (source_ctx)
211 avformat_close_input(&source_ctx);
212 if (dest_ctx)
213 avformat_close_input(&dest_ctx);
214 if (output_ctx) {
215 if (!(output_ctx->oformat->flags & AVFMT_NOFILE))
216 avio_closep(&output_ctx->pb);
217 avformat_free_context(output_ctx);
218 }
219}

◆ is_format_supported()

bool is_format_supported ( const char * filename)

Definition at line 194 of file mxwrite.cpp.

194 {
195 const char *ext = strrchr(filename, '.');
196 if (!ext)
197 return false;
198 // Lowercase the extension for case-insensitive comparison.
199 std::string lower_ext(ext);
200 std::transform(lower_ext.begin(), lower_ext.end(), lower_ext.begin(), [](unsigned char c) { return std::tolower(c); });
201 static const char *kSupported[] = {".mp4", ".mkv", ".mov", ".avi", ".m4v", ".ts", ".mts", ".m2ts", ".mpg", ".mpeg", ".flv", ".f4v", ".3gp", ".3g2", ".wmv", ".asf", ".vob"};
202 for (const char *s : kSupported) {
203 if (lower_ext == s)
204 return true;
205 }
206 return false;
207}

◆ 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 221 of file mxwrite.cpp.

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

◆ 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 958 of file mxwrite.cpp.

958 {
959 const std::string name(encoder_name);
960 const AVCodec *codec = avcodec_find_encoder_by_name(name.c_str());
961 if (!codec || codec->type != AVMEDIA_TYPE_VIDEO) {
962 return {};
963 }
964 AVCodecContext *context = avcodec_alloc_context3(codec);
965 if (!context) {
966 return {};
967 }
968 std::vector<EncoderOptionInfo> result;
969 std::set<std::string> seen;
970 append_encoder_options(context->priv_data, result, seen);
971 append_encoder_options(context, result, seen);
972 avcodec_free_context(&context);
973 std::sort(result.begin(), result.end(), [](const EncoderOptionInfo &left, const EncoderOptionInfo &right) { return left.name < right.name; });
974 return result;
975}
void append_encoder_options(void *object, std::vector< EncoderOptionInfo > &result, std::set< std::string > &seen)
Definition mxwrite.cpp:876
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 192 of file mxwrite.cpp.