Copy audio from one video file to another.
234 {
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
354 return;
355 }
356 }
357 if (avformat_write_header(output_ctx, nullptr) < 0) {
358 std::cerr << "Failed to write header\n";
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);
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);
409 return;
410 }
411 }
412 av_packet_unref(&packet);
413 }
414 av_write_trailer(output_ctx);
416 std::remove(destVideoFile.data());
417 std::rename(temp_output.c_str(), destVideoFile.data());
418}
std::mutex transfer_audio_mutex
bool is_format_supported(const char *filename)
void cleanup_contexts(AVFormatContext *source_ctx, AVFormatContext *dest_ctx, AVFormatContext *output_ctx)
Free FFmpeg format contexts used during transfer operations.