使用FFmpeg 4.x API实现音视频数据混流的基本步骤:
1. 初始化FFmpeg库,创建AVFormatContext上下文对象。
2. 使用avformat_open_input函数打开输入音视频文件,并读取音视频流信息。
3. 使用avformat_alloc_output_context2函数创建输出音视频文件的AVFormatContext上下文。
4. 使用avio_open函数打开输出音视频文件的输出流。
5. 遍历输入音视频的每个stream,根据codec_id创建新的AVStream,并为其设置参数。
6. 使用avformat_write_header函数向输出文件中写入头信息。
7. 使用av_interleaved_write_frame函数将音频和视频数据交错输出到输出文件中。
8. 在遍历完所有frame后,使用av_write_trailer函数结束输出音视频文件。
9. 最后,释放资源并关闭输入输出流和相关的AVFormatContext就可以了。
需要注意的是使用av_interleaved_write_frame
函数将音频和视频数据交错输出时,必须保证视频帧和音频帧的时间戳相对应,否则可能会导致混流出错。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
extern "C" {
#include <libavformat/avformat.h>
}
int main(int argc, char** argv) {
const char* out_filename = "output.mp4";
const char* in_vfilename = "video.mp4";
const char* in_afilename = "audio.mp4";
int ret;
AVFormatContext* in_vformat_ctx;
AVFormatContext* in_aformat_ctx;
AVFormatContext* out_format_ctx;
if ((ret = avformat_open_input(&in_vformat_ctx, in_vfilename, NULL, NULL)) < 0 ||
(ret = avformat_find_stream_info(in_vformat_ctx, NULL)) < 0) {
printf("Failed to open video input file %s: %s\n", in_vfilename, av_err2str(ret));
return ret;
}
if ((ret = avformat_open_input(&in_aformat_ctx, in_afilename, NULL, NULL)) < 0 ||
(ret = avformat_find_stream_info(in_aformat_ctx, NULL)) < 0) {
printf("Failed to open audio input file %s: %s\n", in_afilename, av_err2str(ret));
return ret;
}
avformat_alloc_output_context2(&out_format_ctx, NULL, NULL, out_filename);
if (!out_format_ctx) {
printf("Could not create output context\n");
return AVERROR_UNKNOWN;
}
for (uint32_t i = 0; i < in_vformat_ctx->nb_streams; i++) {
AVStream* in_stream = in_vformat_ctx->streams[i];
AVCodec* codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
if (!codec) {
printf("Unsupported video codec!\n");
return AVERROR_INVALIDDATA;
}
AVStream* out_stream = avformat_new_stream(out_format_ctx, codec);
if (!out_stream) {
printf("Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
if (ret < 0) {
printf("Failed to copy video codec parameters to output stream: %s\n", av_err2str(ret));
return ret;
}
}
for (uint32_t i = 0; i < in_aformat_ctx->nb_streams; i++) {
AVStream* in_stream = in_aformat_ctx->streams[i];
AVCodec* codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
if (!codec) {
printf("Unsupported audio codec!\n");
return AVERROR_INVALIDDATA;
}
AVStream* out_stream = avformat_new_stream(out_format_ctx, codec);
if (!out_stream) {
printf("Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
if (ret < 0) {
printf("Failed to copy audio codec parameters to output stream: %s\n", av_err2str(ret));
return ret;
}
}
if (!(out_format_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&out_format_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
printf("Could not open output file '%s': %s\n", out_filename, av_err2str(ret));
return ret;
}
}
ret = avformat_write_header(out_format_ctx, NULL);
if (ret < 0) {
printf("Error occurred when opening output file: %s\n", av_err2str(ret));
return ret;
}
AVPacket packet;
while (av_read_frame(in_vformat_ctx, &packet) >= 0) {
int stream_index = packet.stream_index;
packet.stream_index = av_stream_get_codec_parameters(out_format_ctx->streams[stream_index])->codec_tag;
av_interleaved_write_frame(out_format_ctx, &packet);
av_packet_unref(&packet);
}
while (av_read_frame(in_aformat_ctx, &packet) >= 0) {
int stream_index = packet.stream_index;
packet.stream_index = av_stream_get_codec_parameters(out_format_ctx->streams[in_aformat_ctx->nb_streams + stream_index])->codec_tag;
av_interleaved_write_frame(out_format_ctx, &packet);
av_packet_unref(&packet);
}
av_write_trailer(out_format_ctx);
if (!(out_format_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&out_format_ctx->pb);
}
avformat_close_input(&in_vformat_ctx);
avformat_close_input(&in_aformat_ctx);
if (out_format_ctx && !(out_format_ctx->flags & AVFMT_NOFILE)) {
avformat_free_context(out_format_ctx);
}
return 0;
}