在音视频处理过程中,有时候我们需要将视频文件中的音频流和视频流拆分出来进行单独处理。这里介绍一下如何通过ffmpeg库将视频文件中音频流和视频流抽取出来保存成单独的文件。音频流和视频流的抽取流程如下图所示:
通过FFmpeg库抽取音频流的示例如下所示,示例代码仅在windows平台上使用mp4格式的视频文件做了验证,如在其它场景下调用有异常,欢迎反馈。
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int err_code;
char errors[1024];
//输入视频地址和输出音频地址
char *src_filename = NULL; //test.mp4
char *dst_filename = NULL; //audio.aac
//输出文件流
FILE *dst_fd = NULL;
int len;
int audio_stream_index = -1; //音频流索引
AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *output_fmt = NULL;
AVFormatContext *fmt_ctx = NULL;
AVFrame *frame = NULL;
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);//设置日志的输出级别
if (argc < 3) {
av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
return -1;
}
src_filename = argv[1];
dst_filename = argv[2];
if (src_filename == NULL || dst_filename == NULL) {
av_log(NULL, AV_LOG_DEBUG, "src or dts file is null!\n");
return -1;
}
//创建输出文件
dst_fd = fopen(dst_filename, "wb");
if (!dst_fd) {
av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s\n", dst_filename);
return -1;
}
//打开媒体文件分配上下文
if ((err_code = avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)) < 0) {
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
src_filename,
err_code,
errors);
return -1;
}
//获取流信息
if ((err_code = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)\n",
src_filename,
err_code,
errors);
return -1;
}
//分配帧数据
frame = av_frame_alloc();
if (!frame) {
av_log(NULL, AV_LOG_DEBUG, "Could not allocate frame\n");
return AVERROR(ENOMEM);
}
//初始化数据包
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//查找最好的音频流
audio_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (audio_stream_index < 0) {
av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
av_get_media_type_string(AVMEDIA_TYPE_AUDIO),
src_filename);
return AVERROR(EINVAL);
}
//循环读取流中的数据包并写入文件
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == audio_stream_index)
{
len = fwrite(pkt.data, 1, pkt.size, dst_fd);
if (len != pkt.size) {
av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
len,
pkt.size);
}
}
av_packet_unref(&pkt);
}
//关闭上下文和文件输出流
avformat_close_input(&fmt_ctx);
if (dst_fd) {
fclose(dst_fd);
}
return 0;
}
通过FFmpeg库抽取视频流的示例如下所示,示例代码仅在windows平台上使用mp4格式的视频文件做了验证,如在其它场景下调用有异常,欢迎反馈。
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int err_code;
char errors[1024];
char *src_filename = NULL;//test.mp4
char *dst_filename = NULL;//output.h264
FILE *dst_fd = NULL;
int video_stream_index = -1;
AVFormatContext *fmt_ctx = NULL;
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);
if (argc < 3) {
av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
return -1;
}
src_filename = argv[1];
dst_filename = argv[2];
if (src_filename == NULL || dst_filename == NULL) {
av_log(NULL, AV_LOG_ERROR, "src or dts file is null, plz check them!\n");
return -1;
}
dst_fd = fopen(dst_filename, "wb");
if (!dst_fd) {
av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s\n", dst_filename);
return -1;
}
//打开多媒体文件
if ((err_code = avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)) < 0) {
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
src_filename,
err_code,
errors);
return -1;
}
//输出媒体信息
av_dump_format(fmt_ctx, 0, src_filename, 0);
//初始化数据包
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//获取视频流
video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_index < 0) {
av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
av_get_media_type_string(AVMEDIA_TYPE_VIDEO),
src_filename);
return AVERROR(EINVAL);
}
//读取数据包
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == video_stream_index) {
int len = fwrite(pkt.data, 1, pkt.size, dst_fd);
if (len != pkt.size) {
av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
len,
pkt.size);
}
}
//release ref
av_packet_unref(&pkt);
}
//释放资源
avformat_close_input(&fmt_ctx);
if (dst_fd) {
fclose(dst_fd);
}
return 0;
}
h264格式的视频流文件,在windows上无法直接播放,可以使用ffplay进行播放,播放命令如下所示:
ffplay test.h264