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.hpp File Reference

FFmpeg-based video writer used by MXWrite. More...

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/hwcontext.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <chrono>
#include <condition_variable>
#include <atomic>
#include <mutex>
#include <queue>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
#include <cstdint>

Go to the source code of this file.

Classes

struct  EncodeOptions
 User-configurable video encoder quality options. More...
struct  Frame_Data
 Queue entry that stores a frame pointer and its capture timestamp. More...
struct  EncodeOptions::HdrInfo
 HDR output options. More...
class  Writer
 FFmpeg-backed RGBA video writer. More...

Functions

void cleanup_contexts (AVFormatContext *source_ctx, AVFormatContext *dest_ctx, AVFormatContext *output_ctx)
 Free FFmpeg format contexts used during transfer operations.
void transfer_audio (std::string_view sourceAudioFile, std::string_view destVideoFile)
 Copy audio from one video file to another.

Detailed Description

FFmpeg-based video writer used by MXWrite.

Definition in file mxwrite.hpp.

Function Documentation

◆ cleanup_contexts()

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

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}

◆ transfer_audio()

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

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