MXVK Vulkan Framework 0.24.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 <iostream>
#include <numeric>
#include <string>
#include <thread>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/opt.h>
#include <libavutil/mastering_display_metadata.h>
#include <libswscale/swscale.h>

Go to the source code of this file.

Namespaces

namespace  anonymous_namespace{mxwrite.cpp}

Functions

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.
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)
bool is_format_supported (const char *filename)
bool anonymous_namespace{mxwrite.cpp}::is_valid_x264_preset (const std::string &p)
std::string anonymous_namespace{mxwrite.cpp}::lowercase_ascii (std::string text)
float anonymous_namespace{mxwrite.cpp}::pqOetf (float L)
float anonymous_namespace{mxwrite.cpp}::srgbEotf (float v)
void transfer_audio (std::string_view sourceAudioFile, std::string_view destVideoFile)
 Copy audio from one video file to another.
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

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

222 {
223 if (source_ctx)
224 avformat_close_input(&source_ctx);
225 if (dest_ctx)
226 avformat_close_input(&dest_ctx);
227 if (output_ctx) {
228 if (!(output_ctx->oformat->flags & AVFMT_NOFILE))
229 avio_closep(&output_ctx->pb);
230 avformat_free_context(output_ctx);
231 }
232}

◆ is_format_supported()

bool is_format_supported ( const char * filename)

Definition at line 200 of file mxwrite.cpp.

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

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

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

Variable Documentation

◆ transfer_audio_mutex

std::mutex transfer_audio_mutex

Definition at line 198 of file mxwrite.cpp.