FFmpeg 是一个开源的多媒体框架,能够解码、编码、转码、封装、解封装、流媒体、滤镜和播放几乎所有的多媒体格式。libavformat 是 FFmpeg 中的一个库,专门用于处理多媒体容器格式,支持多种协议,包括 RTMP(Real Time Messaging Protocol)。
RTMP 是一种基于 TCP 的流媒体传输协议,主要用于在线直播。它允许将音频、视频和数据从服务器推送到客户端,或者从客户端拉取到服务器。
libavformat 提供了对 RTMP 协议的支持,允许开发者通过 FFmpeg 进行 RTMP 流的读取和写入。
在 libavformat 中修改 RTMP 流通常涉及以下几个步骤:
以下是一个简单的示例,展示如何在 FFmpeg 中读取 RTMP 流并将其保存为本地文件:
#include <libavformat/avformat.h>
#include <libavutil/timestamp.h>
#include <libavutil/mathematics.h>
int main(int argc, char *argv[]) {
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
const char *in_filename, *out_filename;
int ret, video_stream_index = -1;
AVStream *out_vid_strm;
if (argc < 3) {
fprintf(stderr, "Usage: %s <input rtmp url> <output file>\n", argv[0]);
return -1;
}
in_filename = argv[1];
out_filename = argv[2];
av_register_all();
// 打开输入流
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
}
// 获取流信息
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
// 查找视频流
for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
break;
}
}
if (video_stream_index == -1) {
fprintf(stderr, "Could not find video stream in the input, aborting\n");
ret = -1;
goto end;
}
// 打开输出文件
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
// 复制流到输出上下文
out_vid_strm = avformat_new_stream(ofmt_ctx, NULL);
if (!out_vid_strm) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
// 复制编解码器参数
if ((ret = avcodec_parameters_copy(out_vid_strm->codecpar, ifmt_ctx->streams[video_stream_index]->codecpar)) < 0) {
fprintf(stderr, "Failed to copy codec parameters\n");
goto end;
}
out_vid_strm->codecpar->codec_tag = 0;
// 打开输出文件
if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", out_filename);
goto end;
}
}
// 写文件头
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
// 读取并写入数据包
while (1) {
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
/* copy packet */
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
av_packet_unref(&pkt);
}
// 写文件尾
av_write_trailer(ofmt_ctx);
end:
if (ifmt_ctx) {
avformat_close_input(&ifmt_ctx);
}
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&ofmt_ctx->pb);
}
if (ofmt_ctx) {
avformat_free_context(ofmt_ctx);
}
return ret;
}
通过以上步骤和示例代码,可以在 FFmpeg 中有效地处理 RTMP 流。根据具体需求,可能需要进一步调整和优化代码。
领取专属 10元无门槛券
手把手带您无忧上云