使用 FFMPEG 库接收 RTSP 流
Posted
技术标签:
【中文标题】使用 FFMPEG 库接收 RTSP 流【英文标题】:Receiving RTSP stream using FFMPEG library 【发布时间】:2012-05-29 17:16:15 【问题描述】:我的 LAN 上有一个使用 RTSP 流式传输视频的 IPCamera。我已经能够使用 ffplay 命令成功捕获并显示它:
ffplay rtsp://admin:123456@192.168.2.50:7070
(有认证)
所以我想使用 ffmpeg
库在 C/C++ 中进行编程来实现相同的目的。我想这一定是可能的。
所以让我提出两个简单的问题:
如何使用 FFMPEG 库在 C/C++ 程序中接收流? (只需提供一些 URL/教程,因为 google 没有帮助)
如何显示收到的视频? (这里也一样,一些很好的 URL 可以指导我)。
【问题讨论】:
【参考方案1】:FWIW,我已经更新了@technique 提供的代码,以使用我从 FFMPEG 3.2.2 获得的库。希望这有助于有人谷歌搜索。有一些小的变化可能会让那些偶然发现这个答案的人感到困惑。
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <sstream>
extern "C"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libswscale/swscale.h>
int main(int argc, char** argv)
// Open the initial context variables that are needed
SwsContext *img_convert_ctx;
AVFormatContext* format_ctx = avformat_alloc_context();
AVCodecContext* codec_ctx = NULL;
int video_stream_index;
// Register everything
av_register_all();
avformat_network_init();
//open RTSP
if (avformat_open_input(&format_ctx, "rtsp://134.169.178.187:8554/h264.3gp",
NULL, NULL) != 0)
return EXIT_FAILURE;
if (avformat_find_stream_info(format_ctx, NULL) < 0)
return EXIT_FAILURE;
//search video stream
for (int i = 0; i < format_ctx->nb_streams; i++)
if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
video_stream_index = i;
AVPacket packet;
av_init_packet(&packet);
//open output file
AVFormatContext* output_ctx = avformat_alloc_context();
AVStream* stream = NULL;
int cnt = 0;
//start reading packets from stream and write them to file
av_read_play(format_ctx); //play RTSP
// Get the codec
AVCodec *codec = NULL;
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec)
exit(1);
// Add this to allocate the context by codec
codec_ctx = avcodec_alloc_context3(codec);
avcodec_get_context_defaults3(codec_ctx, codec);
avcodec_copy_context(codec_ctx, format_ctx->streams[video_stream_index]->codec);
std::ofstream output_file;
if (avcodec_open2(codec_ctx, codec, NULL) < 0)
exit(1);
img_convert_ctx = sws_getContext(codec_ctx->width, codec_ctx->height,
codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24,
SWS_BICUBIC, NULL, NULL, NULL);
int size = avpicture_get_size(AV_PIX_FMT_YUV420P, codec_ctx->width,
codec_ctx->height);
uint8_t* picture_buffer = (uint8_t*) (av_malloc(size));
AVFrame* picture = av_frame_alloc();
AVFrame* picture_rgb = av_frame_alloc();
int size2 = avpicture_get_size(AV_PIX_FMT_RGB24, codec_ctx->width,
codec_ctx->height);
uint8_t* picture_buffer_2 = (uint8_t*) (av_malloc(size2));
avpicture_fill((AVPicture *) picture, picture_buffer, AV_PIX_FMT_YUV420P,
codec_ctx->width, codec_ctx->height);
avpicture_fill((AVPicture *) picture_rgb, picture_buffer_2, AV_PIX_FMT_RGB24,
codec_ctx->width, codec_ctx->height);
while (av_read_frame(format_ctx, &packet) >= 0 && cnt < 1000) //read ~ 1000 frames
std::cout << "1 Frame: " << cnt << std::endl;
if (packet.stream_index == video_stream_index) //packet is video
std::cout << "2 Is Video" << std::endl;
if (stream == NULL) //create stream in file
std::cout << "3 create stream" << std::endl;
stream = avformat_new_stream(output_ctx,
format_ctx->streams[video_stream_index]->codec->codec);
avcodec_copy_context(stream->codec,
format_ctx->streams[video_stream_index]->codec);
stream->sample_aspect_ratio =
format_ctx->streams[video_stream_index]->codec->sample_aspect_ratio;
int check = 0;
packet.stream_index = stream->id;
std::cout << "4 decoding" << std::endl;
int result = avcodec_decode_video2(codec_ctx, picture, &check, &packet);
std::cout << "Bytes decoded " << result << " check " << check
<< std::endl;
if (cnt > 100) //cnt < 0)
sws_scale(img_convert_ctx, picture->data, picture->linesize, 0,
codec_ctx->height, picture_rgb->data, picture_rgb->linesize);
std::stringstream file_name;
file_name << "test" << cnt << ".ppm";
output_file.open(file_name.str().c_str());
output_file << "P3 " << codec_ctx->width << " " << codec_ctx->height
<< " 255\n";
for (int y = 0; y < codec_ctx->height; y++)
for (int x = 0; x < codec_ctx->width * 3; x++)
output_file
<< (int) (picture_rgb->data[0]
+ y * picture_rgb->linesize[0])[x] << " ";
output_file.close();
cnt++;
av_free_packet(&packet);
av_init_packet(&packet);
av_free(picture);
av_free(picture_rgb);
av_free(picture_buffer);
av_free(picture_buffer_2);
av_read_pause(format_ctx);
avio_close(output_ctx->pb);
avformat_free_context(output_ctx);
return (EXIT_SUCCESS);
它可以被编译成这样的效果:
g++ -w my_streamer.cpp -o my_streamer $(pkg-config --cflags --libs libavcodec libavformat libswscale libavutil)
我将-w
包括在内,因为库中的这些函数有很多弃用警告。您需要安装 pkg-config 以及 libavcodec libavformat libswscale 和 libavutil 库。 (h/t @BobCheng 缺少图书馆)。
【讨论】:
自从您的更新发生了一些变化。请考虑添加新版本。谢谢:-) @FolkertvanHeusden 我只使用 3.2.2(而且我已经有一段时间不需要这样做了),所以很遗憾我没有了解任何新功能或更改的最新信息 :( 次要编辑:您需要 libavcodec 和 libavutil 才能成功编译:g++ -w my_streamer.cpp -o my_streamer $(pkg-config --cflags --libs libavcodec libavformat libswscale libavutil) 欣赏它@BobCheng - 不确定我的设置是否允许我出于某种原因不包括那些但仍然有效。我已将其添加到任何人复制/粘贴的答案中【参考方案2】:FFmpeg 可以像打开本地视频文件一样直接打开 rtsp 流。 Here 是使用最新版本的 FFmpeg 更新的教程代码。
【讨论】:
【参考方案3】:检查您的 ./configure 文件。可能是为armlinux
而不是为x86
编译。这就是为什么你得到未定义的引用 'avcodec_register_all'
这里是解决方案:-
$ cd ffmpeg
$export LD_RUN_PATH=/usr/local/lib:/usr/lib:/lib
$ ./configure
$ make && make install
然后编译你的应用程序。
【讨论】:
【参考方案4】:对于 rtsp 流,以下内容对我有用(接收帧后我将结果保存到 ppm 文件):
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <sstream>
extern "C"
#include <avcodec.h>
#include <avformat.h>
#include <avio.h>
#include <swscale.h>
void log_callback(void *ptr, int level, const char *fmt, va_list vargs)
static char message[8192];
const char *module = NULL;
if (ptr)
AVClass *avc = *(AVClass**) ptr;
module = avc->item_name(ptr);
vsnprintf_s(message, sizeof(message), fmt, vargs);
std::cout << "LOG: " << message << std::endl;
int main(int argc, char** argv)
SwsContext *img_convert_ctx;
AVFormatContext* context = avformat_alloc_context();
AVCodecContext* ccontext = avcodec_alloc_context();
int video_stream_index;
av_register_all();
avformat_network_init();
//av_log_set_callback(&log_callback);
//open rtsp
if(avformat_open_input(&context, "rtsp://134.169.178.187:8554/h264.3gp",NULL,NULL) != 0)
return EXIT_FAILURE;
if(avformat_find_stream_info(context,NULL) < 0)
return EXIT_FAILURE;
//search video stream
for(int i =0;i<context->nb_streams;i++)
if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
video_stream_index = i;
AVPacket packet;
av_init_packet(&packet);
//open output file
//AVOutputFormat* fmt = av_guess_format(NULL,"test2.mp4",NULL);
AVFormatContext* oc = avformat_alloc_context();
//oc->oformat = fmt;
//avio_open2(&oc->pb, "test.mp4", AVIO_FLAG_WRITE,NULL,NULL);
AVStream* stream=NULL;
int cnt = 0;
//start reading packets from stream and write them to file
av_read_play(context);//play RTSP
AVCodec *codec = NULL;
codec = avcodec_find_decoder(CODEC_ID_H264);
if (!codec) exit(1);
avcodec_get_context_defaults3(ccontext, codec);
avcodec_copy_context(ccontext,context->streams[video_stream_index]->codec);
std::ofstream myfile;
if (avcodec_open(ccontext, codec) < 0) exit(1);
img_convert_ctx = sws_getContext(ccontext->width, ccontext->height, ccontext->pix_fmt, ccontext->width, ccontext->height,
PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
int size = avpicture_get_size(PIX_FMT_YUV420P, ccontext->width, ccontext->height);
uint8_t* picture_buf = (uint8_t*)(av_malloc(size));
AVFrame* pic = avcodec_alloc_frame();
AVFrame* picrgb = avcodec_alloc_frame();
int size2 = avpicture_get_size(PIX_FMT_RGB24, ccontext->width, ccontext->height);
uint8_t* picture_buf2 = (uint8_t*)(av_malloc(size2));
avpicture_fill((AVPicture *) pic, picture_buf, PIX_FMT_YUV420P, ccontext->width, ccontext->height);
avpicture_fill((AVPicture *) picrgb, picture_buf2, PIX_FMT_RGB24, ccontext->width, ccontext->height);
while(av_read_frame(context,&packet)>=0 && cnt <1000)
//read 100 frames
std::cout << "1 Frame: " << cnt << std::endl;
if(packet.stream_index == video_stream_index)//packet is video
std::cout << "2 Is Video" << std::endl;
if(stream == NULL)
//create stream in file
std::cout << "3 create stream" << std::endl;
stream = avformat_new_stream(oc,context->streams[video_stream_index]->codec->codec);
avcodec_copy_context(stream->codec,context->streams[video_stream_index]->codec);
stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio;
int check = 0;
packet.stream_index = stream->id;
std::cout << "4 decoding" << std::endl;
int result = avcodec_decode_video2(ccontext, pic, &check, &packet);
std::cout << "Bytes decoded " << result << " check " << check << std::endl;
if(cnt > 100)//cnt < 0)
sws_scale(img_convert_ctx, pic->data, pic->linesize, 0, ccontext->height, picrgb->data, picrgb->linesize);
std::stringstream name;
name << "test" << cnt << ".ppm";
myfile.open(name.str());
myfile << "P3 " << ccontext->width << " " << ccontext->height << " 255\n";
for(int y = 0; y < ccontext->height; y++)
for(int x = 0; x < ccontext->width * 3; x++)
myfile << (int)(picrgb->data[0] + y * picrgb->linesize[0])[x] << " ";
myfile.close();
cnt++;
av_free_packet(&packet);
av_init_packet(&packet);
av_free(pic);
av_free(picrgb);
av_free(picture_buf);
av_free(picture_buf2);
av_read_pause(context);
avio_close(oc->pb);
avformat_free_context(oc);
return (EXIT_SUCCESS);
【讨论】:
感谢您的回复。我按照these 的说明安装了ffmpeg 和x264。安装成功,库安装在我的ubuntu系统的/usr/lib下。当我试图编译你的代码(来自/home/bhanu/main.cpp)时,我遇到了一些与适当标志有关的问题,我不断收到链接器错误。我会发布它们,请指导我错误是什么/在哪里? 我使用的命令是g++ main.cpp -lavcodec -lavdevice -lavfilter -lavformat -lavutil -logg -lrtmp -lswscale -lx264 -lpthread -lvorbis -L/usr/lib
,我得到以下输出main.cpp: In function 'int main(int, char**)': /home/bhanu/work_environment/softwares/ffmpeg/libavformat/allformats.c:49: undefined reference to 'avcodec_register_all' /usr/lib/libavformat.a(oggparsevorbis.o): In function 'vorbis_packet': collect2: ld returned 1 exit status
和一些类似的错误。完整的编译器输出请参考链接here。
cnt <1000) //read 100 frames
看起来代码读取了 1000 帧而不是...
我为使用 FFMPEG 解码 RTSP 流期间生成的错误日志创建了一个要点。 gist.github.com/anonymous/64b3df25b7f103c0da54 知道如何解决上述错误日志。显示的图片模糊。
@technique 这对我很有帮助,我添加了一个更新版本作为新答案,它与 ffmpeg 3.2.2 的库一起使用,以防有人通过 Google 找到它。他们更改了一些函数和枚举,这些函数和枚举一开始让我陷入了困境。以上是关于使用 FFMPEG 库接收 RTSP 流的主要内容,如果未能解决你的问题,请参考以下文章
使用 ffmpeg 将低延迟 RTSP 视频流式传输到 android