热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

FFmpeg代码实现最简版本小咖秀

今天代码实现了从两个音视频文件中分别抽取音频、视频数据,并将这两种数据存储在一个新的MP4文件中,文件可以正常播放画面和声音,也就是实现一

今天代码实现了从两个音视频文件中分别抽取音频、视频数据,并将这两种数据存储在一个新的MP4文件中,文件可以正常播放画面和声音,也就是实现一个最简版本的小咖秀。

前面几篇博客已经把需要的知识都讲了一遍,代码流程也基本一致,下面的代码看起来也会非常轻松。

源码实现:

int ret = -1;
int err_code;
char errors[ERROR_STR_SIZE];char *src_file1, *src_file2, *out_file;AVFormatContext *ifmt_ctx1 = NULL;
AVFormatContext *ifmt_ctx2 = NULL;AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *ofmt = NULL;AVStream *in_stream1 = NULL;
AVStream *in_stream2 = NULL;AVStream *out_stream1 = NULL;
AVStream *out_stream2 = NULL;int audio_stream_index = 0;
int vedio_stream_indes = 0;// 文件最大时长,保证音频和视频数据长度一致
double max_duration = 0;AVPacket pkt;int stream1 = 0, stream2 = 0;av_log_set_level(AV_LOG_DEBUG);src_file1 = argv[1];
src_file2 = argv[2];
out_file = argv[3];//打开两个输入文件
if ((err_code &#61; avformat_open_input(&ifmt_ctx1, src_file1, 0, 0)) <0) {av_strerror(err_code, errors, ERROR_STR_SIZE);av_log(NULL, AV_LOG_ERROR,"Could not open src file, %s, %d(%s)\n",src_file1, err_code, errors);goto END;
}if ((err_code &#61; avformat_open_input(&ifmt_ctx2, src_file2, 0, 0)) <0) {av_strerror(err_code, errors, ERROR_STR_SIZE);av_log(NULL, AV_LOG_ERROR,"Could not open the second src file, %s, %d(%s)\n",src_file2, err_code, errors);goto END;
}//创建输出上下文
if ((err_code &#61; avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_file)) <0) {av_strerror(err_code, errors, ERROR_STR_SIZE);av_log(NULL, AV_LOG_ERROR, "Failed to create an context of outfile , %d(%s) \n",err_code, errors);
}ofmt &#61; ofmt_ctx->oformat;// 找到第一个参数里最好的音频流和第二个文件中的视频流下标
audio_stream_index &#61; av_find_best_stream(ifmt_ctx1, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
vedio_stream_indes &#61; av_find_best_stream(ifmt_ctx2, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);// 获取第一个文件中的音频流
in_stream1 &#61; ifmt_ctx1->streams[audio_stream_index];
stream1 &#61; 0;
// 创建音频输出流
out_stream1 &#61; avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream1) {av_log(NULL, AV_LOG_ERROR, "Failed to alloc out stream!\n");goto END;
}
// 拷贝流参数
if ((err_code &#61; avcodec_parameters_copy(out_stream1->codecpar, in_stream1->codecpar)) <0) {av_strerror(err_code, errors, ERROR_STR_SIZE);av_log(NULL, AV_LOG_ERROR,"Failed to copy codec parameter, %d(%s)\n",err_code, errors);
}out_stream1->codecpar->codec_tag &#61; 0;// 获取第二个文件中的视频流
in_stream2 &#61; ifmt_ctx2->streams[vedio_stream_indes];
stream2 &#61; 1;// 创建视频输出流
out_stream2 &#61; avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream2) {av_log(NULL, AV_LOG_ERROR, "Failed to alloc out stream!\n");goto END;
}// 拷贝流参数
if ((err_code &#61; avcodec_parameters_copy(out_stream2->codecpar, in_stream2->codecpar)) <0) {av_strerror(err_code, errors, ERROR_STR_SIZE);av_log(NULL, AV_LOG_ERROR,"Failed to copy codec parameter, %d(%s)\n",err_code, errors);goto END;
}out_stream2->codecpar->codec_tag &#61; 0;av_dump_format(ofmt_ctx, 0, out_file, 1);// 判断两个流的长度&#xff0c;确定最终文件的长度
if (in_stream1->duration * av_q2d(in_stream1->time_base) > in_stream2->duration * av_q2d(in_stream2->time_base)) {max_duration &#61; in_stream2->duration * av_q2d(in_stream2->time_base);
} else {max_duration &#61; in_stream1->duration * av_q2d(in_stream1->time_base);
}//打开输出文件
if (!(ofmt->flags & AVFMT_NOFILE)) {if ((err_code &#61; avio_open(&ofmt_ctx->pb, out_file, AVIO_FLAG_WRITE)) <0) {av_strerror(err_code, errors, ERROR_STR_SIZE);av_log(NULL, AV_LOG_ERROR,"Could not open output file, %s, %d(%s)\n",out_file, err_code, errors);goto END;}
}//写头信息
avformat_write_header(ofmt_ctx, NULL);av_init_packet(&pkt);// 读取音频数据并写入输出文件中
while (av_read_frame(ifmt_ctx1, &pkt) >&#61; 0) {// 如果读取的时间超过了最长时间表示不需要该帧&#xff0c;跳过if (pkt.pts * av_q2d(in_stream1->time_base) > max_duration) {av_packet_unref(&pkt);continue;}// 如果是我们需要的音频流&#xff0c;转换时间基后写入文件if (pkt.stream_index &#61;&#61; audio_stream_index) {pkt.pts &#61; av_rescale_q_rnd(pkt.pts, in_stream1->time_base, out_stream1->time_base,(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.dts &#61; av_rescale_q_rnd(pkt.dts, in_stream1->time_base, out_stream1->time_base,(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.duration &#61; av_rescale_q(max_duration, in_stream1->time_base, out_stream1->time_base);pkt.pos &#61; -1;pkt.stream_index &#61; stream1;av_interleaved_write_frame(ofmt_ctx, &pkt);av_packet_unref(&pkt);}
}// 读取视频数据并写入输出文件中
while (av_read_frame(ifmt_ctx2, &pkt) >&#61; 0) {// 如果读取的时间超过了最长时间表示不需要该帧&#xff0c;跳过if (pkt.pts * av_q2d(in_stream2->time_base) > max_duration) {av_packet_unref(&pkt);continue;}// 如果是我们需要的视频流&#xff0c;转换时间基后写入文件if (pkt.stream_index &#61;&#61; vedio_stream_indes) {pkt.pts &#61; av_rescale_q_rnd(pkt.pts, in_stream2->time_base, out_stream2->time_base,(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.dts &#61; av_rescale_q_rnd(pkt.dts, in_stream2->time_base, out_stream2->time_base,(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt.duration &#61; av_rescale_q(max_duration, in_stream2->time_base, out_stream2->time_base);pkt.pos &#61; -1;pkt.stream_index &#61; stream2;av_interleaved_write_frame(ofmt_ctx, &pkt);av_packet_unref(&pkt);}
}//写尾信息
av_write_trailer(ofmt_ctx)&#xff1b;ret &#61; 0;END:
// 释放内存
if (ifmt_ctx1) {avformat_close_input(&ifmt_ctx1);
}if (ifmt_ctx2) {avformat_close_input(&ifmt_ctx2);
}if (ofmt_ctx) {if (!(ofmt->flags & AVFMT_NOFILE)) {avio_closep(&ofmt_ctx->pb);}avformat_free_context(ofmt_ctx);
}

最终效果如下&#xff0c;从左到右&#xff1a;输入1、输入2、输出文件。输出文件拿到了输入1的音频、输入2的视频信息&#xff0c;并且时长为输入2的时长。

目前只是在做C语言开发&#xff0c;后面会慢慢把重心移到Android平台上&#xff0c;在FFmpeg和Android平台的基础上玩更多有意思的东西。



推荐阅读
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 你的问题在于:1. 代码格式混乱,缺乏必要的缩进,导致可读性极低;2. 使用 `strlen()` 和 `malloc()` 函数时,必须包含相应的头文件;3. `write()` 函数的返回值处理不当,建议检查并处理其返回值以确保程序的健壮性。此外,建议在编写代码时遵循良好的编程规范,增加代码的可维护性和可读性。 ... [详细]
  • 【问题】在Android开发中,当为EditText添加TextWatcher并实现onTextChanged方法时,会遇到一个问题:即使只对EditText进行一次修改(例如使用删除键删除一个字符),该方法也会被频繁触发。这不仅影响性能,还可能导致逻辑错误。本文将探讨这一问题的原因,并提供有效的解决方案,包括使用Handler或计时器来限制方法的调用频率,以及通过自定义TextWatcher来优化事件处理,从而提高应用的稳定性和用户体验。 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
  • Python默认字符解析:深入理解Python中的字符串处理
    在Python中,字符串是编程中最基本且常用的数据类型之一。尽管许多初学者是从C语言开始接触字符串,通常通过经典的“Hello, World!”程序入门,但Python对字符串的处理方式更为灵活和强大。本文将深入探讨Python中的字符串处理机制,包括字符串的创建、操作、格式化以及编码解码等方面,帮助读者全面理解Python字符串的特性和应用。 ... [详细]
  • 详解 Qt 串口通信程序全程图文 (4)
    Qt串口通信程序全程图文是本文介绍的内容,本文一开始先讲解对程序的改进,在文章最后将要讲解一些重要问题。1、在窗口中加入一些组合框ComboBox&# ... [详细]
  • 无论是计算机专业学生还是非计算机专业的学习者,在掌握C语言的过程中可能会遇到诸多挑战,不清楚从何入手。为此,本文系统地梳理了2019年福建省C语言的核心知识点,并结合最新的技术进展进行了详细总结,旨在为初学者提供全面的学习指导。文章不仅涵盖了基础语法和数据结构,还深入探讨了指针、内存管理和算法优化等高级主题,帮助读者快速提升编程能力。 ... [详细]
  • 在Java编程中,利用Scanner类可以有效地接收和处理用户输入。本文介绍了Scanner类的基本概念及其使用方法,重点讲解了三种常用的输入方式,并提供了详细的代码示例和注意事项,帮助开发者更好地理解和应用这一功能。 ... [详细]
  • Android开发常见问题汇总(含Gradle解决方案)第二篇
    本文继续深入探讨Android开发中常见的问题及其解决方案,特别聚焦于Gradle相关的挑战。通过详细分析和实例演示,帮助开发者高效解决构建过程中的各种难题,提升开发效率和项目稳定性。 ... [详细]
author-avatar
G版车臣
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有