c++ ffmpeg进行rtsp 推流
时间: 2023-11-03 17:58:15 浏览: 434
在 C++ 中使用 FFmpeg 进行 RTSP 推流,主要需要以下步骤:
1. 初始化 FFmpeg 库:调用 av_register_all() 函数注册 FFmpeg 库中的所有可用文件格式和编解码器。
2. 打开输入流:调用 avformat_open_input() 函数打开 RTSP 输入流,获取输入流的相关信息。
3. 查找视频流:调用 avformat_find_stream_info() 函数查找视频流的相关信息。
4. 查找编码器:调用 avcodec_find_encoder() 函数查找编码器,以便将视频流编码为指定格式。
5. 创建输出格式上下文:调用 avformat_alloc_output_context2() 函数创建输出格式上下文。
6. 添加视频流:调用 avformat_new_stream() 函数创建一个新的视频流。
7. 打开输出流:调用 avio_open2() 函数打开输出流。
8. 写文件头:调用 avformat_write_header() 函数将输出格式上下文中的头部信息写入输出流中。
9. 循环读取视频帧:调用 av_read_frame() 函数循环读取视频帧。
10. 编码视频帧:调用 avcodec_encode_video2() 函数将读取的视频帧编码为指定格式。
11. 写入编码后的帧数据:调用 av_write_frame() 函数将编码后的帧数据写入输出流中。
12. 写文件尾:调用 av_write_trailer() 函数将输出格式上下文的尾部信息写入输出流中。
13. 释放资源:释放所有资源。
以下是一个简单的示例代码:
```C++
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <unistd.h>
extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavutil/error.h>
}
#define RTSP_URL "rtsp://localhost:8554/test.sdp" // RTSP 输入地址
#define OUTPUT_URL "rtmp://localhost:1935/live/test" // RTMP 输出地址
int main(int argc, char *argv[])
{
av_register_all(); // 注册所有可用文件格式和编解码器
AVFormatContext *ifmt_ctx = NULL;
int ret = 0;
// 打开 RTSP 输入流
if ((ret = avformat_open_input(&ifmt_ctx, RTSP_URL, NULL, NULL)) < 0)
{
std::cerr << "Could not open input stream " << RTSP_URL << std::endl;
return ret;
}
// 查找视频流信息
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0)
{
std::cerr << "Could not find stream information" << std::endl;
return ret;
}
AVCodecContext *codec_ctx = NULL;
AVCodec *codec = NULL;
// 查找 H.264 编码器
codec = avcodec_find_encoder_by_name("libx264");
if (!codec)
{
std::cerr << "Could not find h264 encoder" << std::endl;
return AVERROR(EINVAL);
}
// 创建编码器上下文
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx)
{
std::cerr << "Could not allocate codec context" << std::endl;
return AVERROR(ENOMEM);
}
// 设置编码器参数
codec_ctx->codec_id = codec->id;
codec_ctx->bit_rate = 400000;
codec_ctx->width = 640;
codec_ctx->height = 480;
codec_ctx->time_base = {1, 25};
codec_ctx->gop_size = 10;
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
// 打开编码器
if ((ret = avcodec_open2(codec_ctx, codec, NULL)) < 0)
{
std::cerr << "Could not open codec" << std::endl;
return ret;
}
AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *ofmt = NULL;
// 创建输出格式上下文
avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", OUTPUT_URL);
ofmt = ofmt_ctx->oformat;
// 添加视频流
AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
out_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
out_stream->codecpar->codec_id = codec_ctx->codec_id;
out_stream->codecpar->bit_rate = codec_ctx->bit_rate;
out_stream->codecpar->width = codec_ctx->width;
out_stream->codecpar->height = codec_ctx->height;
avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);
// 打开输出流
if (!(ofmt->flags & AVFMT_NOFILE))
{
if ((ret = avio_open2(&ofmt_ctx->pb, OUTPUT_URL, AVIO_FLAG_WRITE, NULL, NULL)) < 0)
{
std::cerr << "Could not open output URL " << OUTPUT_URL << std::endl;
return ret;
}
}
// 写文件头
if ((ret = avformat_write_header(ofmt_ctx, NULL)) < 0)
{
std::cerr << "Error writing header" << std::endl;
return ret;
}
int video_stream_index = 0;
AVPacket pkt = {0};
// 循环读取视频帧
while (true)
{
if ((ret = av_read_frame(ifmt_ctx, &pkt)) < 0)
{
break;
}
if (pkt.stream_index == video_stream_index)
{
// 编码视频帧
if ((ret = avcodec_send_packet(codec_ctx, &pkt)) < 0)
{
std::cerr << "Error sending packet to encoder" << std::endl;
break;
}
// 写入编码后的帧数据
while (ret >= 0)
{
ret = avcodec_receive_frame(codec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
std::cerr << "Error receiving frame from encoder" << std::endl;
goto end;
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
// 将编码后的帧数据写入输出流
if ((ret = avcodec_send_frame(codec_ctx, frame)) < 0)
{
std::cerr << "Error sending frame to encoder" << std::endl;
goto end;
}
while (ret >= 0)
{
ret = avcodec_receive_packet(codec_ctx, &pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
std::cerr << "Error receiving packet from encoder" << std::endl;
goto end;
}
av_packet_rescale_ts(&pkt, codec_ctx->time_base, out_stream->time_base);
pkt.stream_index = video_stream_index;
if ((ret = av_write_frame(ofmt_ctx, &pkt)) < 0)
{
std::cerr << "Error writing packet to output stream" << std::endl;
goto end;
}
}
av_packet_unref(&pkt);
}
}
av_packet_unref(&pkt);
}
// 写文件尾
av_write_trailer(ofmt_ctx);
end:
// 释放资源
avcodec_free_context(&codec_ctx);
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
return 0;
}
```
需要注意的是,该示例代码并未完全测试,仅供参考,具体实现还需要根据实际情况进行调整。同时,需要注意 FFmpeg 的版本问题,不同版本的 API 可能存在差异。
阅读全文