使用FFMPEG库接收RTSP流

我使用RTSP在LAN流video上有一个IPCamera。 我已经能够使用ffplay命令成功捕获并显示它:

ffplay rtsp://admin:123456@192.168.2.50:7070 

(带身份validation)

所以我想使用ffmpeg库在C / C ++中使用编程实现相同的function。 我想这一定是可能的。

那么让我说两个简单的问题:

  1. 如何使用FFMPEG库在C / C ++程序中接收流? (只是提供一些URL /教程,因为谷歌没有帮助)

  2. 如何显示收到的video? (在这里,一些很好的URL指导我)。

对于rtsp流,以下是为我工作(在接收帧后,我将结果保存为ppm文件):

  #include  #include  #include  #include  #include  extern "C" { #include  #include  #include  #include  } 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;inb_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); } 

FWIW,我更新了@technique提供的代码,以便使用我从FFMPEG 3.2.2获得的库。 希望这有助于谷歌搜索这个。 对于那些绊倒这个答案的人来说,有一些微小的变化可能令人困惑。

 #include  #include  #include  #include  #include  extern "C" { #include  #include  #include  #include  } 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 libavformat libswscale) 

我包含-w因为库中的这些函数有很多弃用警告。 您需要安装pkg-config以及libavformat和libswscale库。

FFmpeg可以直接打开rtsp流,就像打开本地video文件一样。 这是一个使用最新版本的FFmpeg进行更新的教程代码。

检查./configure文件。 可能是针对armlinux编译而不是x86 。 这就是为什么你得到'avcodec_register_all'未定义引用

这是解决方案: –

 $ cd ffmpeg $export LD_RUN_PATH=/usr/local/lib:/usr/lib:/lib $ ./configure $ make && make install 

然后编译你的申请。