Copy audio from one video file to another.
221 {
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
339 return;
340 }
341 }
342 if (avformat_write_header(output_ctx, nullptr) < 0) {
343 std::cerr << "Failed to write header\n";
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);
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);
394 return;
395 }
396 }
397 av_packet_unref(&packet);
398 }
399 av_write_trailer(output_ctx);
401 std::remove(destVideoFile.data());
402 std::rename(temp_output.c_str(), destVideoFile.data());
403}
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.