live555接实时视频流+ffmpeg解+x264再编

如本文题目所述,文中介绍一下如何用接收rtsp实时流后对实时流进行解码,解码成yuv420,再由x264编译。

    live555 中的例子testRTSPClient.cpp 提供了一个创建rtsp客户端接收视频流的例子,使用该例子可从支持rtsp协议的摄像机获取视频流。因为我用的相机提供的是h264编码的实时流,并且我希望在接收实时流的时候保存录像,因此对testRTSPClient.cpp进行了简单的修改。 continueAfterSETUP 函数中,进行如下修改:

  1. scs.subsession->sink=H264VideoFileSink::createNew(env,"test.h264",NULL,550000,false);  
    使用H264VideoFileSink处理接收到的h264视频流。(我用的相机多是只有视频,如果有音频需要根据实际情况处理,本文所做的相应编码全部是只接收视频流的。如果对于有音频的相机,只想接收视频流可以在MediaSession.cpp  的createSourceObjects 函数中,在if (createSimpleRTPSource) 之前把createSimpleRTPSource 置为 false)。经过后期反思,可以自己继承filesink后添加和自己业务相关的逻辑。

    H264VideoFileSink继承了FileSink,实际上,每一帧视频都是在filesink中写入test.h264文件的。因此在filesink中进行处理。void FileSink::addData(unsigned char const* data, unsigned dataSize,struct timeval presentationTime) 函数的传入参数,unsigned char const* data, unsigned dataSize 分别是一帧的内容和一帧的长度(这个视频帧没有h264的开头0001,void H264VideoFileSink::afterGettingFrame 这个函数中有添加0001的 代码)。FileSink::addData 中获取的一帧视频(接收到的rtp包,有一个mark算一帧,因此NAL nal_unit_type中的6(SEI)、7(SPS)、8(PPS)都是分开的)。关于如何判断帧类型,可以利用可以利用data[1]和0x1f进行位或运算。在用ffmpeg解码时,sps,pps和之后的idr帧是要一起传入解码器的,其他的sei我是直接丢弃,其实也可以sps+pps+sei+idr一起传入ffmpeg。先上一段代码,这段代码用于将需要解码的一帧存入一个缓存区中,之后ffmpeg 解码缓存区的数据。

进行如下定义:

  1. typedef struct   
  2. {  
  3.     unsigned char * realbuf;//存储接收到的完整一帧  
  4.     int realsize;//帧长度  
  5. }mybuf;//存储一帧  
  6.   
  7. #define  bufnum 200  
  8. mybuf encodebuf[bufnum];  
        
  1. if (fOutFid != NULL && data != NULL) {  
  2.   int iRet=-1;    
  3.   iRet=data[0] & 0x1f;  
  4.  if ((iRet==6||iRet==9)&&(m_start==1))//sei等直接丢弃  
  5.   {  
  6.       // fwrite(data, 1, dataSize, fOutFid);  
  7.       return ;  
  8.   }  
  9.   if ((iRet==7)&&(m_start==1))  
  10.   {  
  11.       m_pos=0;  
  12.       memmove(encodebuf[index].realbuf,sHead,4);  
  13.       encodebuf[index].realsize+=4;  
  14.       memmove(encodebuf[index].realbuf+ encodebuf[index].realsize,data,dataSize);  
  15.       encodebuf[index].realsize+=dataSize;  
  16.   }  
  17.   if ((iRet==8)&&(m_start==1))//pps  
  18.   {  
  19.        
  20.       memmove(encodebuf[index].realbuf+encodebuf[index].realsize,sHead,4);  
  21.       encodebuf[index].realsize+=4;  
  22.       memmove(encodebuf[index].realbuf+ encodebuf[index].realsize,data,dataSize);  
  23.       encodebuf[index].realsize+=dataSize;  
  24.   }  
  25.   if((iRet==7)&&(m_start==0))//sps  
  26.   {  
  27.       memmove(encodebuf[index].realbuf,sHead,4);  
  28.       encodebuf[index].realsize+=4;  
  29.     //  fwrite(sHead,1,4,fOutFid);  
  30.       memmove(encodebuf[index].realbuf+ 4,data,dataSize);  
  31.       encodebuf[index].realsize+=dataSize;  
  32.        
  33.       m_start++;  
  34.   }  
  35.  if (m_start>0)  
  36.   {  
  37.     //  fwrite(data, 1, dataSize, fOutFid);  
  38.       if (iRet!=0&&iRet!=7&&iRet!=8)  
  39.       {  
  40.           memmove(encodebuf[index].realbuf+ encodebuf[index].realsize,sHead,4);  
  41.           encodebuf[index].realsize+=4;  
  42.           memmove(encodebuf[index].realbuf+ encodebuf[index].realsize,data,dataSize);  
  43.           encodebuf[index].realsize+=dataSize;  
  44.           if (iRet==5)//idr  
  45.           {  
  46.               m_pos=1;  
  47.           }  
  48.           if (m_pos==1)  
  49.           {  
  50.               index++;  
  51.              if (encodebuf[index-1].realsize==0)  
  52.              {  
  53.                  printf("");  
  54.              }  
  55.                  
  56.               PostThreadMessage(ThreadID,MY_MSG_WRITE,index,0);  
  57.               if (index==bufnum)  
  58.               {  
  59.                   index=0;  
  60.               }  
  61.           }  
  62.             
  63.             
  64.       }  
  65.         
  66.   }  
  67.    
  68.  }  

以上是在live555中针对拼凑解码一帧的修改逻辑。再重复一次:解码的第一帧要把sps+pps+idr传入ffmpeg进行解码,因此要把这三帧拼到一起。再从ffmpeg解码说起,本人对ffmpeg的学习,大多倚仗雷霄骅在csdn上的博客,具体对ffmpeg的讲解就不在赘述,雷神在csdn上对ffmpeg有很好的讲解。简而言之就是初始化ffmpeg 相关的变量后调用avcodec_decode_video2函数解码视频。

直接上解码的那小段代码,说明一下:

  1. char* input=encodebuf[myindex].realbuf;          
  2. int nLen=encodebuf[myindex].realsize;  
  3. AVFrame *frame = NULL;  
  4. frame = av_frame_alloc();  
  5. AVPacket packet;  
  6. av_init_packet(&packet);  
  7. packet.size = nLen;//一帧的长度  
  8. packet.data = input;//从缓冲区取出的一帧  
  9. packet.pts=i_pts;  
  10. if (!frame) {  
  11. ret = AVERROR(ENOMEM);  
  12. }  
  13. ret = avcodec_decode_video2(dec_ctx, frame,&got_frame, &packet);  
经过以上过程,解码后的yuv数据就到了frame中 。yuv可以用来进行重新编码,也可以直接用来显示:如用opengl或者d3d进行渲染。

    对应该文,上传一份完整的测试代码,用了live555接收实时视频,ffmpeg 解码, x264编码,rtp打包 。代码比较乱,测试用,不过文中所述内容在代码中都有体现。上传的代码因为平时测试用所以有些地方有比较粗暴的修改,比如H264VideoFileSink中删除了一些函数,还有FileSink种的一些修改。

//-----------------------------------------------x264-----------------------------------------------------//

   如果接受实时流并解码,解码成yuv后可以用x264重新编码···如果想压缩原视频,只要编码后的比原来小就行(可能这是最水的视频压缩了)。其实用x264库能做的编码,ffmpeg也可以,秉着多用一个开源库,多学习一点的态度就用下x264吧。我的x264是下载的最新版本,在windows 下自己编译的,主要参考了http://www.cnblogs.com/ark-zhang/archive/2012/11/28/2793154.html 这篇文章.

    从ffmpeg 解码后的yuv存在了AVFrame *frame 中,把其中对应的y,u,v赋值给x264 ,用x264进行编码 .

  

  1.                            {  
  2. pic_in.img.plane[0]=frame->data[0];  
  3. pic_in.img.i_stride[0] = frame->linesize[0];  
  4. pic_in.img.plane[1]=frame->data[1];  
  5. pic_in.img.i_stride[1] = frame->linesize[1];  
  6. pic_in.img.plane[2]=frame->data[2];  
  7. pic_in.img.i_stride[2] = frame->linesize[2];  
  8. //pic_in.i_pts = i_pts++;  
  9. x264_encoder_encode(encoder, &nals, &nnal, &pic_in, &pic_out);  
  10. av_frame_free(&frame);  
  11. //x264_nal_t *nal;  
  12. for ( int i=0; i<nnal; i++) {  
  13.     fwrite(nals[i].p_payload,1,nals[i].i_payload,fp_write);//写编码后的视频  
  14.     printf("encode size=%d\n",nals[i].i_payload);  
  15.   
  16. }  

  1. nals[i].p_payload中就是编码后的一帧。 
目前live555接收实时视频,ffmpeg 解码, x264编码,可以 通过opengl,d3d,sdl做个实时流媒体播放器

CSDN下载地址:http://download.csdn.net/detail/yuanchunsi/9919248

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以通过以下步骤来实现Java与RTSP、FFmpeg、HTML和Nginx的结合来实现视频实时播放的监控系统: 1. 首先,你需要使用Java来创建一个监控系统的后端服务。你可以使用Java的网络编程库来监听RTSP流并将其码。可以使用开源的库,例如JRTSP或者Xuggler来处理RTSP流,并将其转换为可供播放的视频流。 2. 下来,你需要使用FFmpeg来处理视频流FFmpeg是一个强大的多媒体处理工具,可以用于转码、码、编码等操作。你可以使用FFmpeg码RTSP流,并将其转换为HTML5支持的视频格式,例如HLS(HTTP Live Streaming)或者MPEG-DASH(Dynamic Adaptive Streaming over HTTP)。 3. 在前端方面,你可以使用HTML和JavaScript来创建一个简单的视频播放器。你可以使用HTML5的<video>标签来嵌入视频,并使用JavaScript来控制视频的播放、暂停等操作。你可以使用一些开源的视频播放器库,例如video.js或者plyr来简化开发过程。 4. 最后,你可以使用Nginx作为反向代理服务器来提供视频流的分发和缓存功能。Nginx可以将视频流从后端服务器转发给前端浏览器,并且可以缓存视频文件以提高性能和可靠性。你可以配置Nginx来支持HLS或者MPEG-DASH协议,并且可以使用Nginx的HTTP模块来进行性能优化和安全加固。 综上所述,通过将Java、RTSP、FFmpeg、HTML和Nginx结合起来,你可以实现一个监控视频的实时播放系统。这个系统可以从RTSP流中提取视频数据,经过FFmpeg处理后,通过HTML和JavaScript在浏览器中进行播放,并且可以使用Nginx提供性能优化和缓存支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值