当我们使用ffmpeg进行视频推流的时候,流媒体服务器与推流终端一直连接的时候,推流是成功的,但是如果服务器重启,就会出现推流一直失败的问题,av_interleaved_write_frame返回值-32,根据ffmpeg对返回值的解释:

-32:管道阻塞:这个一般是socket错误,推流的服务器断开了socket链接,导致发送失败。

推流程序如果没有断开重连功能的话,就只能关掉程序,重新启动来重新连接服务器解决问题,但这显然不是解决问题的办法,我们期望的办法是程序能够在推流失败后能够自动重连服务器,其实现逻辑如下:

1.启动Init成功,开始推流

2.推流失败,调用stop,清理调用的FFmpeg的环境。

3.重新启动Init,成功后开始推流

ffmpeg关于rtmp推流的代码,网上有很多,我把这些代码修改了下,封装成一个类,名字叫PushRtmp, 其有三个函数:

1.Init,初始化连接服务器

2.Push,推流

3.Stop, 停止推流,清理环境

二话不说,上代码。

头文件:

#pragma once
#include <opencv2/opencv.hpp>
#include <string>
#ifdef _WIN32
// Windows
extern "C" {#include "libavformat/avformat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C" {#endif
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
};
#endif
#endifclass PushRtmp {public:static PushRtmp* getInst();bool Init( std::string url, int width, int height, int fps );void Push( cv::Mat& image );void Stop();private:static PushRtmp* instance;// rtmp服务地址std::string rtmp_url_;int width_;int height_;int fps_;// 输出的数据结构AVFrame* yuv_ = NULL;// 像素格式转换上下文SwsContext* vsc_ = NULL;int vpts         = 0;// 编码器上下文AVCodecContext* vc_ = NULL;// rtmp flv 封装器AVFormatContext* ic_ = NULL;AVPacket pack_;AVStream* vs_ = NULL;
};

cpp文件:

#include "push_rtmp.hpp"
#include <chrono>
#include <exception>
#include <thread>PushRtmp* PushRtmp::instance = nullptr;PushRtmp* PushRtmp::getInst()
{if ( instance == nullptr ){instance = new PushRtmp();}return instance;
}// 初始化函数
// url--推流的地址
// width --帧的宽度
// height --帧的高度
bool PushRtmp::Init( std::string url, int width, int height, int fps )
{rtmp_url_ = url;width_    = width;height_   = height;fps_      = fps;//注册所有的编解码器avcodec_register_all();//注册所有的封装器av_register_all();//注册所有网络协议avformat_network_init();try{int inWidth  = width;int inHeight = height;std::cout <<"+++++++++"<< inWidth << inHeight<<std::endl;/// 2 初始化格式转换上下文vsc_ = sws_getCachedContext( vsc_, inWidth, inHeight, AV_PIX_FMT_BGR24,  //源宽、高、像素格式inWidth, inHeight, AV_PIX_FMT_YUV420P,      //目标宽、高、像素格式SWS_BICUBIC,                                // 尺寸变化使用算法0, 0, 0 );if ( !vsc_ ){printf( "sws_getCachedContext failed!" );return false;}/// 3 初始化输出的数据结构yuv_         = av_frame_alloc();yuv_->format = AV_PIX_FMT_YUV420P;yuv_->width  = inWidth;yuv_->height = inHeight;yuv_->pts    = 0;//分配yuv空间int ret = av_frame_get_buffer( yuv_, 32 );if ( ret != 0 ){char buf[ 1024 ] = { 0 };av_strerror( ret, buf, sizeof( buf ) - 1 );printf( buf );return false;}/// 4 初始化编码上下文// a 找到编码器AVCodec* codec = avcodec_find_encoder( AV_CODEC_ID_H264 );if ( !codec ){printf( "Can`t find h264 encoder!" );return false;}// b 创建编码器上下文vc_ = avcodec_alloc_context3( codec );if ( !vc_ ){printf( "avcodec_alloc_context3 failed!" );return false;}// c 配置编码器参数vc_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;  //全局参数vc_->codec_id     = codec->id;vc_->thread_count = 8;vc_->bit_rate  = 50 * 1024 * 8;  //压缩后每秒视频的bit位大小 50kBvc_->width     = inWidth;vc_->height    = inHeight;vc_->time_base = { 1, fps };vc_->framerate = { fps, 1 };//画面组的大小,多少帧一个关键帧vc_->gop_size     = 50;vc_->max_b_frames = 0;vc_->pix_fmt      = AV_PIX_FMT_YUV420P;// d 打开编码器上下文ret = avcodec_open2( vc_, 0, 0 );if ( ret != 0 ){char buf[ 1024 ] = { 0 };av_strerror( ret, buf, sizeof( buf ) - 1 );printf( buf );return false;}std::cout << "avcodec_open2 success!" << std::endl;/// 5 输出封装器和视频流配置// a 创建输出封装器上下文ret = avformat_alloc_output_context2( &ic_, 0, "flv", url.c_str() );if ( ret != 0 ){char buf[ 1024 ] = { 0 };av_strerror( ret, buf, sizeof( buf ) - 1 );printf( buf );return false;}// b 添加视频流vs_ = avformat_new_stream( ic_, NULL );if ( !vs_ ){printf( "avformat_new_stream failed" );return false;}vs_->codecpar->codec_tag = 0;//从编码器复制参数avcodec_parameters_from_context( vs_->codecpar, vc_ );av_dump_format( ic_, 0, url.c_str(), 1 );///打开rtmp 的网络输出IOret = avio_open( &ic_->pb, url.c_str(), AVIO_FLAG_WRITE );if ( ret != 0 ){char buf[ 1024 ] = { 0 };av_strerror( ret, buf, sizeof( buf ) - 1 );printf( buf );return false;}//写入封装头ret = avformat_write_header( ic_, NULL );if ( ret != 0 ){char buf[ 1024 ] = { 0 };av_strerror( ret, buf, sizeof( buf ) - 1 );printf( buf );return false;}}catch ( std::exception& ex ){// if (cam.isOpened())//    cam.release();if ( vsc_ ){sws_freeContext( vsc_ );vsc_ = NULL;}if ( vc_ ){avio_closep( &ic_->pb );avcodec_free_context( &vc_ );}return false;// std::cerr << ex.what() << endl;}return true;
}void PushRtmp::Push( cv::Mat& image )
{//输入的数据结构uint8_t* indata[ AV_NUM_DATA_POINTERS ] = { 0 };indata[ 0 ]                        = image.data;int insize[ AV_NUM_DATA_POINTERS ] = { 0 };//一行(宽)数据的字节数insize[ 0 ] = image.cols * image.elemSize();int h = sws_scale( vsc_, indata, insize, 0, image.rows,  //源数据yuv_->data, yuv_->linesize );if ( h <= 0 ){return;}/// h264编码yuv_->pts = vpts;vpts++;int ret = avcodec_send_frame( vc_, yuv_ );if ( ret != 0 )return;ret = avcodec_receive_packet( vc_, &pack_ );if ( ret != 0 || pack_.size > 0 ){// cout << "*" << pack.size << flush;}else{return;}//推流pack_.pts      = av_rescale_q( pack_.pts, vc_->time_base, vs_->time_base );pack_.dts      = av_rescale_q( pack_.dts, vc_->time_base, vs_->time_base );pack_.duration = av_rescale_q( pack_.duration, vc_->time_base, vs_->time_base );ret            = av_interleaved_write_frame( ic_, &pack_ );if ( ret == 0 ){// std::cout << "#" << flush;}else{std::cout << "push rtmp failed error code:"<< ret;if ( ret == -32 ){std::cout <<"Server disconnected, start reconnect..." ;Stop();Init( rtmp_url_, width_, height_, fps_ );while ( true ){if ( Init( rtmp_url_, width_, height_, fps_ ) )break;std::this_thread::sleep_for( std::chrono::milliseconds( 5000 ) );}}}
}//停止推流
void PushRtmp::Stop()
{if ( vsc_ ){sws_freeContext( vsc_ );vsc_ = NULL;}if ( vc_ ){avio_closep( &ic_->pb );avcodec_free_context( &vc_ );}avformat_free_context( ic_ );// avformat_close_input(&ifmt_ctx);
}

使用示范代码:

#include "push_rtmp.hpp"
#include <chrono>
#include <iostream>
#include <opencv2/highgui.hpp>
#include <thread>using namespace cv;int main( int argc, char* argv[] )
{VideoCapture cam;Mat frame;cam.open( 0 );if ( !cam.isOpened() ){throw exception("cam open failed!");}namedWindow( "video" );int inWidth  = cam.get( CAP_PROP_FRAME_WIDTH );int inHeight = cam.get( CAP_PROP_FRAME_HEIGHT );int fps      = cam.get( CAP_PROP_FPS );fps          = 25;while ( true ){if ( PushRtmp::getInst()->Init( "rtmp://192.168.123.32/live/24", inWidth, inHeight, fps ) )break;std::this_thread::sleep_for( std::chrono::milliseconds( 1000 ) );}for ( ;; ){///读取rtsp视频帧,解码视频帧if ( !cam.grab() ){continue;}/// yuv转换为rgbif ( !cam.retrieve( frame ) ){continue;}PushRtmp::getInst()->Push( frame );}return 0;
}

ffmpeg推流时与服务器断开后的自动重连功能的实现相关推荐

  1. C3P0,Proxool,BoneCP,Druid等连接池的断开自动重联功能

    数据库连接池的断开自动重联.失败恢复功能显得很重要,不知道目前主流的数据库连接池:C3P0,Proxool,BoneCP,Druid等支持如何? 我知道的: Proxool: ? 1 2 3 4 5 ...

  2. jboss连接池,断开后自动重连功能

    最近客户现场的测试环境连的数据库极不稳定,经常会出现需要重新启动数据库的情况, 但是一旦重启数据库 则会出现 提示 ,执行sql错误,原因就是datasource 没有获取新的连接! 那么解决办法就是 ...

  3. tomcat修改tomcat-users.xml文件,服务器重启后又自动还原

    tomcat配置用户管理权限,修改tomcat-users.xml文件 在tomcat目录中找到/conf/tomcat-users.xml,修改 <tomcat-users>     & ...

  4. 关于因和数据库库断开数据源没有自动重连变无效的问题

    转自 http://agapple.iteye.com/blog/791943 可以后另一篇做对比:http://agapple.iteye.com/blog/772507 同样的内容,不同的描述方式 ...

  5. C3P0,Proxool等连接池的断开自动重联功能

    mysql数据库一般8小时断开一次: Proxool: 01 <!--proxool 解决与数据库断开重连问题(houseKeepingTestSql为oracle的语法,其他数据库类似) -- ...

  6. 计算机考试交卷完后关机重启,电脑常识:电脑为什么关机后又自动重启动

    电脑关机后自动重启的原因最常见的是由快速关机引起的. 解决办法是:①开始运行输入:msconfig确定系统实用配置程序高级选中"禁用快速关机"重新启动电脑. ②如果还不能解决,看看 ...

  7. websocket自动重连

    背景:js中的websocket想要实现自动重连功能,由于js中只需要充当客户端收数据的功能,不需要发送数据,也就没有考虑实现心跳包. var lockReconnect = false;//webs ...

  8. Java 服务端 socket 掉线重启后,客户端自动重连

    目的 强调:setSoTimeout()函数的重要性,目的是为了写出更健壮的程序. 问题:如果客户端发起的socket 在连接后,读取数据流之前,刚好服务器端突然断线了,紧接着又重启了,则当前sock ...

  9. macbook蓝牙pan未连接_蓝牙自动重连机制

    蓝牙自动重连机制的原理分析 在日常使用蓝牙的过程中,想必大家都发现了这样一个现象:连接蓝牙设备的手机在关闭再重新打开蓝牙后,会自动连接上先前连接着的那个设备,同样的场景对于安卓车机系统效果也是一致的. ...

最新文章

  1. android关闭触摸声音,如何在Android中以编程方式禁用触摸时的振动和声音?
  2. 显示网格_快速制图软件 Edraw Max教程:Edraw Max怎么显示出网格线?
  3. Case Study. Technical and Commercial understating. Internal use only.
  4. 实用的IDE工具助力你学好Python
  5. C++ namespace 命名空间
  6. Xshell远程访问工具及epel-release包安装
  7. 《CLR via C#》读书笔记 之 基元类型、引用类型和值类型
  8. 关于安装更新office版本时,需要卸载office所遇到的问题
  9. 2022年华数杯数学建模
  10. 软件测试——测试分类及测试工具的简介
  11. 手把手教你理解SURF算法的全部过程
  12. POJ 2387 Til the Cows Come Home BFS最短路求解
  13. RocksDB 入门
  14. JavaScirpt 与 ECMAScript 的关系
  15. coding码市管理项目
  16. Echarts 图表一些细节设置 lenged 双Y轴等
  17. 人大金仓数据库怎么样?好用吗?
  18. 用matlab模拟机械运动
  19. T滤波器(低通滤波器)
  20. 5G+AI数字化智能工厂建设解决方案

热门文章

  1. boost::mpl模块实现insert相关的测试程序
  2. boost的chrono模块等待按键的测试程序
  3. 基于Boost::beast模块的同步http服务器
  4. Boost:人口 bimap的测试程序
  5. DCMTK:表示细分对象的类
  6. VTK:网格之ClipClosedSurface
  7. VTK:网格之BoundaryEdges
  8. OpenCV基于ARM的Linux系统的交叉编译
  9. C++判断一个数字是否是某个数字的阶乘(附完整源码)
  10. C++继承体系下的对象构造