使用FFmpeg解码,使用SDL显示画面有时候会连续丢包,导致花屏的现象。

解决办法:

1.将rtsp传输方式由默认的udp传输,设置为tcp传输

2.每次解码一帧后,SDL_Delay时长设置为跟帧率匹配,使用1000/帧率作为时长

发现就没有频繁丢包的情况了

代码如下:

DWORD WINAPI ONVIF::ShowVideo(void *param)
{if(param == NULL){MW_DEBUG_ERR("param is err!\n");return -1;//}AVFormatContext *pFormatCtx=NULL;  int             i = 0, videoindex = 0 , y_size = 0 , ret = 0;AVCodecContext  *pCodecCtx=NULL;  AVCodec         *pCodec=NULL;  AVFrame *pFrame=NULL,*pFrameYUV=NULL;  unsigned char *out_buffer=NULL;  SwsContext *img_convert_ctx1;//用于YUV420pSwsContext *img_convert_ctx2;//用于BGR24AVPacket packet;VideoInfo vInfo;memcpy(&vInfo,param,sizeof(VideoInfo));char filepath[MAX_PATH] = {0};strcpy_s(filepath,vInfo.uri);MW_DEBUG_INFO("show video param:\n");MW_DEBUG_INFO("uri:%s\nx:%d\ny:%d\nheight:%d\nwidth:%d\nisShowVideo:%d\n",vInfo.uri,vInfo.x,vInfo.y,vInfo.height,vInfo.width,vInfo.isShowVideo);//视频流处理其实就是从文件中读取一包包的packet,将这一包包的packet组成frame帧av_register_all(); //注册所有文件格式和编解码的库,初始化库,你也可以初始化让它仅仅支持某一种编码格式,但是没必要avformat_network_init(); //是能网络功能,可以从网络上读取流数据 pFormatCtx = avformat_alloc_context();//分配空间,主要存储视音频封装格式中包含的信息//读取文件(或者rtsp地址)信息的头部,并且把信息保存在pFormatCtx中,后面两个NULL用来指定特殊文件格式,这里表示自动检测文件格式//打开视频文件,文件名可以是一个rtsp视频流地址,例如rtsp://admin:kykj1234@192.168.1.222:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1//如果需要登录,其中admin:kykj1234表示用户名和登录密码。avformat_open_input打开视频文件,从视频流中解析出部分信息,填充到pFormatCtx中,pFormatCtx非常重要,里面//不仅包含了视频的分辨率,时间戳等信息,而且包含了相应的解码器的信息

<span style="white-space:pre"> </span><span style="color:#ff0000;">AVDictionary* options = NULL;  av_dict_set(&options, "rtsp_transport", "tcp", 0);  if(avformat_open_input(&pFormatCtx,filepath,NULL/*自动检测文件格式*/,&options)!=0){MW_DEBUG_ERR("Couldn't open input stream : %s\n",filepath);  goto END;return -1;  }</span>//检测文件中的流信息,这个函数为pFormatCtx->streams流信息数据成员填充上正确的信息if(avformat_find_stream_info(pFormatCtx,NULL)<0){MW_DEBUG_ERR("Couldn't find stream information.\n");goto END;return -1;}//找到第一个视频流,因为里面的流还有可能是音频流或者其他的,我们摄像头只关心视频流videoindex=-1;for(i=0; i<(int)pFormatCtx->nb_streams; i++){if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;break;}}if(videoindex==-1){//没有找到视频流printf("Didn't find a video stream.\n");goto END;return -1;}//获取一个合适的编码器pCodecpCodecCtx=pFormatCtx->streams[videoindex]->codec;  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  if(pCodec==NULL){  printf("Codec not found.\n"); goto END;return -1;  }  //打开这个编码器,pCodecCtx表示编码器上下文,里面有流数据的信息if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){  printf("Could not open codec.\n");  return -1;  }  //pFrameYUV分配空间,该函数并没有为AVFrame的像素数据分配空间,需要使用av_image_fill_arrays分配pFrameYUV = av_frame_alloc();if(pFrameYUV == NULL){goto END;return -1;}int numBytes;numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUVJ420P , pCodecCtx->width , pCodecCtx->height , 1);out_buffer = (uint8_t*)av_malloc(sizeof(uint8_t) * numBytes);//avpicture_fill((AVPicture*)pFrameYUV , out_buffer , AV_PIX_FMT_YUVJ420P,pCodecCtx->width , pCodecCtx->height);av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,  AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);  SDL_Window *window = nullptr;//对应的就是原来的SDL_SurfaceSDL_Renderer *ren = nullptr;SDL_Rect rect;SDL_Texture *texture = NULL;if(vInfo.isShowVideo){/*====================SDL init start=====================*/if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {    printf( "Could not initialize SDL - %s\n", SDL_GetError());   goto END;return -1;  }   window = SDL_CreateWindow("NetCamera", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,vInfo.width/*pCodecCtx->width*/,vInfo.height /*pCodecCtx->height*/, SDL_WINDOW_OPENGL);if (!window){cout << SDL_GetError() << endl;goto END;return -1;}SDL_SetWindowBordered(window ,SDL_FALSE);SDL_SetWindowPosition(window,vInfo.x,vInfo.y);//创建渲染器,渲染器和窗口联系起来了ren = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if (ren == nullptr){cout << SDL_GetError() << endl;goto END;return -1;}//创建文理,文理和渲染器联系起来了,一个文理对应一帧图片数据texture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_YV12,SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);rect.x = 0, rect.y = 0;rect.w = pCodecCtx->width;rect.h = pCodecCtx->height;}//*************************************************************////通过读取包来读取整个视频流,然后把它解码成帧,最后转换格式并且保存int frameFinished;//int psize = pCodecCtx->width * pCodecCtx->height;//分配一个packet,用于存储从视频流中读取的原始的还没有解码的数据,大小刚好为一帧av_new_packet(&packet, numBytes);av_init_packet(&packet);//output file informationcout << "-----------------------------文件信息----------------------------------" << endl;av_dump_format(pFormatCtx, 0, filepath, 0);cout << "-----------------------------------------------------------------------" << endl;i = 0;int tmp_width = vInfo.width,tmp_height = vInfo.height;if(-1 == vInfo.width){tmp_width = pCodecCtx->width;}if(-1 == vInfo.height){tmp_height = pCodecCtx->height;}//sws_getContext是初始化函数,初始化你需要转换的格式,目的是为了获取返回的SwsContext指针变量,给后面的sws_scale使用//sws_scale会根据你初始化的信息来转换视频格式,可以改变视频格式,也可以改变视频的分辨率,因为如果想要窗口缩小,需要将//分辨率改成相应大小//1.这里是将解码后的视频流转换成YUV420Pimg_convert_ctx1 = sws_getContext(pCodecCtx->width/*视频宽度*/, pCodecCtx->height/*视频高度*/,pCodecCtx->pix_fmt/*像素格式*/, tmp_width/*pCodecCtx->width*//*目标宽度*/,tmp_height/* pCodecCtx->height*//*目标高度*/, AV_PIX_FMT_YUV420P/*目标格式*/,SWS_BICUBIC/*图像转换的一些算法*/, NULL, NULL, NULL);//===============================================start=================================================//pFrameRGB分配空间,该函数并没有为AVFrame的像素数据分配空间,需要使用av_image_fill_arrays分配AVFrame *pFrameRGB = av_frame_alloc();if(pFrameRGB == NULL){goto END;return -1;}int rgbSize;rgbSize = av_image_get_buffer_size(AV_PIX_FMT_BGR24 ,tmp_width/* pCodecCtx->width*/ ,tmp_height /*pCodecCtx->height*/ , 1);unsigned char *rgb_buffer = (uint8_t*)av_malloc(sizeof(uint8_t) * rgbSize);av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize,rgb_buffer,  AV_PIX_FMT_BGR24 ,tmp_width, tmp_height,1); //2.这里是将解码后的视频流转换成RGBimg_convert_ctx2 = sws_getContext(pCodecCtx->width/*视频宽度*/, pCodecCtx->height/*视频高度*/,pCodecCtx->pix_fmt/*像素格式*/, tmp_width/*pCodecCtx->width*//*目标宽度*/,tmp_height/* pCodecCtx->height*//*目标高度*/,AV_PIX_FMT_BGR24/*目标格式*/,SWS_BICUBIC/*图像转换的一些算法*/, NULL, NULL, NULL);cout << "视频流像素格式:"<<pCodecCtx->pix_fmt<<endl;cout << "宽度:"<<pCodecCtx->width <<" 高度:"<<pCodecCtx->height<<endl;cout << "rgbsize:"<<rgbSize<<endl;
//===============================================end=================================================pFrame=av_frame_alloc();//分配帧内存,用来保存帧,存储从packet解码后的帧数据if(pFrame == NULL){goto END;}//Read the next frame of a streamwhile (av_read_frame(pFormatCtx, &packet) >= 0){//读取原始数据(此时还没有解码)放到packet中if(b_exit_thread){break;}EventProc();//在显示视频的线程里面必须要相应事件处理,否则一旦点击窗口会卡死//Is this a packet from the video stream?//如果这个是一个视频流数据if (packet.stream_index == videoindex){//decode video frame of size packet.size from packet.data into picture//解码一帧视频数据,在这个里面把packet数据解码放到了pFrame中ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);//Did we get a video frame?if (ret >= 0){//Convert the image from its native format to YUVif (0 != frameFinished){//这个标志表示已经都去了一个完整帧,因为读取一个packet不一定就是一个完整帧,如果不完整需要继续读取packet//===============================================start 开始将YUV数据转换成RGB并保存图片==============//将视频流数据转换成RGB格式//pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);//pFrame->linesize[0] *= -1;//pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);//pFrame->linesize[1] *= -1;//pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);//pFrame->linesize[2] *= -1;int h = sws_scale(img_convert_ctx2 ,(const uint8_t* const*)pFrame->data/*源数据地址*/,pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data , pFrameRGB->linesize);pthread_mutex_lock (&mutex);ret = SaveBmp24(NULL,tmp_width,tmp_height,pFrameRGB->data[0]);pthread_mutex_unlock(&mutex);//===============================================end=================================================/*格式转换函数,可以转换视频格式,也可以用来改变视频的分辨率*/sws_scale(img_convert_ctx1, (const uint8_t* const*)pFrame->data/*源数据地址*/,pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data/*目标数据地址*/, pFrameYUV->linesize);if(vInfo.isShowVideo){SDL_UpdateYUVTexture(texture, &rect, pFrameYUV->data[0], pFrameYUV->linesize[0],//Y分量pFrameYUV->data[1], pFrameYUV->linesize[1],//U分量pFrameYUV->data[2], pFrameYUV->linesize[2]);//V分量SDL_RenderClear(ren);SDL_RenderCopy(ren, texture, &rect, &rect);SDL_RenderPresent(ren);//SDL_Delay(10);//这里的值不能太大,太大的话会出现花屏的现象}}}else{cout << "decode failed" << endl;goto END;}<span style="color:#ff0000;">SDL_Delay(40);//这里的值要合适,用1000/帧率,比如25fps,则设置为1000/25 = 40</span>av_packet_unref(&packet);}END:if(NULL != out_buffer){av_free(out_buffer);out_buffer = NULL;}if(NULL != pFrame){av_frame_free(&pFrame);pFrame = NULL;}if(NULL != pFrameYUV){av_frame_free(&pFrameYUV);pFrameYUV = NULL;}if(NULL != pCodecCtx){avcodec_close(pCodecCtx);pCodecCtx = NULL;}if(NULL != pCodec){}if(NULL != pFormatCtx){avformat_close_input(&pFormatCtx);pFormatCtx = NULL;}if(NULL != img_convert_ctx1){sws_freeContext(img_convert_ctx1);img_convert_ctx1 = NULL;}if(NULL != img_convert_ctx2){sws_freeContext(img_convert_ctx2);img_convert_ctx1 = NULL;}if(NULL != window){SDL_DestroyWindow(window);window = NULL;}if(NULL != ren){SDL_DestroyRenderer(ren);ren = NULL;}if(NULL != texture){SDL_DestroyTexture(texture);texture = NULL;}if(NULL != packet.data){av_free_packet(&packet);}b_exit_thread_finished = true;//说明线程退出完毕return 0;
}

使用FFmpeg解码 丢包 花屏相关推荐

  1. ffmpeg 切片花屏_利用ffmpeg解码H264,花屏,该如何解决

    利用ffmpeg解码H264,花屏 UINT CMP4File::VideoCap_Thread_Fun(void* pParam) { CMP4File  *pMP4File=(CMP4File*) ...

  2. h264 丢包花屏处理的一个想法

    本文 来自 csdn lidp http://blog.csdn.net/perfectpdl, 转载注明出处,谢谢. H264视频在分组网络中传输丢包不可避免,尤其在网络环境不好时传输h264码流, ...

  3. 如何做带宽估计和丢包策略

    1 建立线性模型 使用RTP 包发送 RTCP包回馈拿到延时时间,计算抖动,什么是抖动呢,多个数据包之间的延时不相同就叫抖动,非常简单,第一次发送延时20ms, 第二次发送延时10ms, 第三次发送延 ...

  4. w806开发板驱动ov2640读取jpeg图片1600x1200分辨率,以及花屏原因及解决办法

    主频需要160MHz以上,80MHz主频读取会丢数据,读取过程中要关闭所有中断否则会出现丢数据花屏现象,还有一个重要的地方需要注意,PCLK速度过慢同时照片信息量多时,jpeg文件过大也会花一部分,像 ...

  5. 安卓miracast花屏_Miracast无线投屏技术优缺点有哪些?

    Miracast无线投屏技术优缺点有哪些? 当下主流与的无线投屏技术主要有Miracast.苹果(Apple)的AirPlay屏幕镜像,WIF联盟的DLNA(组织已经解散),虽然还有一些其他的无线投屏 ...

  6. ffmpeg h264解码, 屏蔽因为网络丢包等各种原因导致的花屏帧

     ffmpeg h264解码, 屏蔽因为网络丢包等各种原因导致的花屏帧  ---->看来问题只能这样解决了,现在还要多测测,防止产生新的问题.目前来看,对现有代码没有影响,花屏的帧直接屏蔽掉了. ...

  7. 结合实战,浅析GB/T28181(八)——视频丢包(卡顿、花屏、绿屏)排查

    1 问题现象 在视频专网(局域网)中,通过GB/T 28181视频平台接入大量的网络摄像机,比如上百.上千,甚至上万台.当系统同一时刻实况点播并发的视频路数较多时,常常会在客户端或电视墙监视器上,出现 ...

  8. ffmpeg解码花屏

    问题: 解码为YUV420转为Bitmap后显示在屏幕上时,有三分之二为花屏:如图: 首先用h264Visa分析帧: 已经读出了sps等信息,这些信在解码第一帧时被写入环境变量中,所以下边的帧不需要s ...

  9. 不丢包的 却花屏_用户观看视频业务出现花屏故障

    1.组网情况 如图所示,Switch的一端和组播服务器相连,并且将GE1/0/0加入Eth-Trunk 0,另一端下挂用户终端,并且将GE1/0/2加入Eth-Trunk 1.在Switch上部署了组 ...

最新文章

  1. 帝国CMS的phomenewspic/ecmsinfo标签详解
  2. 关于map的下标操作的2个例子
  3. 基于Matlab的循环码实验报告,基于MATLAB的循环码实验报告
  4. 在PHP服务器上使用JavaScript进行缓慢的Loris攻击[及其预防措施!]
  5. [html] Ajax与Flash的优缺点分别是什么?
  6. Redis 如何实现主从复制
  7. python treemap_使用TreeMap
  8. oracle迁移 rman,ORACLE RMAN迁移
  9. SAP PP制造生产教程
  10. 新版个人所得税计算python_最新个税计算 / 个税计算器 小程序 wepy 开发
  11. c++虚指针及相应对象的数据结构
  12. oracle批量修改多张表的数据,Oracle批量修改用户表table的表空间
  13. Python报错TypeError: Descriptors cannot not be created directly
  14. PHP 换行符的一些说明
  15. jzoj3424. 【NOIP2013模拟】粉刷匠
  16. C++ Style and Technique FAQ (
  17. 【FCC】检查字符串结尾
  18. 一个小小的互联网创业狗的自白
  19. 没关系,是爱情啊 经典台词
  20. ORB-SLAM2的源码阅读(十一):LoopClosing类

热门文章

  1. java中got是什么意思_java – 为什么我应该使用url.openStream而不是url.getContent?
  2. HTML-table标签属性
  3. 初次安装mysql8 设置密码为root
  4. 请安息吧,史蒂夫 乔布斯
  5. 网络下载-xUtils,HttpUtils
  6. TVM:成为深度学习领域的“Linux”
  7. AudioPolicyService启动过程分析(android_audio)
  8. kali_study
  9. 互联网晚报 | 450个小城市超9成房价不过万元;巴奴火锅创始人再回应“9元5片土豆”;华兴资本控股:控股股东包凡目前正配合调查...
  10. 【一步步学OpenGL 18】 -《漫射光》