Qt版的Rtsp客户端

来源:互联网 发布:cia安装软件 编辑:程序博客网 时间:2024/05/17 12:05

其实这个实现还是很简单的。主要是要在编译ffmpeg的时候,开启rtsp,network,这样我们就可以直接利用avformat_open_input函数接受rtsp协议了。ffmpeg代码的编写的流程和输入文件是一样的。所以说整个实现过程还是比较简单的。同样,我这里会给出我在开发这个客户端的所有的参考资料。对于别人详细介绍的知识我就不会在赘述了。

   随便提一下,我一开始使用live555作为客户端,接受到的数据传给ffmpeg解码。但是解码现实的视频会花屏。经过测试x264编码一帧图像会产生多个slice,这也就是为什么有的NAL前面的起始码是0 0 0 1有的NAL却是0 0 1。作为第一个slice一般是用0 0 0 1,其余的则用0 0 1。而live555的frame是一个去掉了RTP包头的,以及去掉了NAL四个字节的起始码(主要是发送端去掉的)。而我通过ffmpeg测试发现,如果每次给一个NAL让ffmpeg解码是会出现错误的,应该给的是一个完整的一帧数据,也就是几个NAL(slice)。虽然实验是得出这个结论,但是我用live555接受到的数据进行组帧,发现还是无法解码。我目前还没有发现是什么问题,我想可能还是我自己某个细节没有注意到。后来听说ffmpeg可以直接接受rtsp流媒体,就测试了一下,发现OK了,于是qt版的Rtsp客户端就出来了。

   还是老样子,先给出一些我感觉比较有价值的资料链接吧。

   H.264中NAL、Slice与frame意思及相互关系 http://blog.chinaunix.net/uid-20235103-id-1970924.html。我觉得是对做流媒体来说还是比较有用的东西。

   qt我也是现学现用的,这里面还是给我一个qt的学习链接,主要是使用qtcreator来编写。http://bbs.qter.org/forum.php?mod=viewthread&tid=193

   ffmpeg的学习,感觉网上的资料还真是满丰富的。这里就给出文件的ffmpeg播放。后面我会介绍ffmpeg播放rtsp的。http://blog.csdn.net/leixiaohua1020/article/details/8652605


下面就是前面利用前面一篇文章写的rtsp服务器的。





[cpp] view plain copy
  1. /* 
  2.  * FFmpeg.cpp 
  3.  * 
  4.  *  Created on: 2014年2月25日 
  5.  *      Author: ny 
  6.  */  
  7.   
  8. #include "FFmpeg.h"  
  9.   
  10.   
  11.   
  12. FFmpeg::FFmpeg()  
  13. {  
  14.     pCodecCtx = NULL;  
  15.     videoStream=-1;  
  16.   
  17. }  
  18.   
  19. FFmpeg::~FFmpeg()  
  20. {  
  21.     sws_freeContext(pSwsCtx);  
  22. }  
  23.   
  24. int FFmpeg::initial(QString & url)  
  25. {  
  26.     int err;  
  27.     rtspURL=url;  
  28.     AVCodec *pCodec;  
  29.     av_register_all();  
  30.     avformat_network_init();  
  31.     pFormatCtx = avformat_alloc_context();  
  32.     pFrame=avcodec_alloc_frame();  
  33.     err = avformat_open_input(&pFormatCtx, rtspURL.toStdString().c_str(), NULL,  
  34.                               NULL);  
  35.     if (err < 0)  
  36.     {  
  37.         printf("Can not open this file");  
  38.         return -1;  
  39.     }  
  40.     if (av_find_stream_info(pFormatCtx) < 0)  
  41.     {  
  42.         printf("Unable to get stream info");  
  43.         return -1;  
  44.     }  
  45.     int i = 0;  
  46.     videoStream = -1;  
  47.     for (i = 0; i < pFormatCtx->nb_streams; i++)  
  48.     {  
  49.         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)  
  50.         {  
  51.             videoStream = i;  
  52.             break;  
  53.         }  
  54.     }  
  55.     if (videoStream == -1)  
  56.     {  
  57.         printf("Unable to find video stream");  
  58.         return -1;  
  59.     }  
  60.     pCodecCtx = pFormatCtx->streams[videoStream]->codec;  
  61.   
  62.     width=pCodecCtx->width;  
  63.     height=pCodecCtx->height;  
  64.     avpicture_alloc(&picture,PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);  
  65.     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);  
  66.     pSwsCtx = sws_getContext(width, height, PIX_FMT_YUV420P, width,  
  67.             height, PIX_FMT_RGB24,  
  68.             SWS_BICUBIC, 0, 0, 0);  
  69.   
  70.     if (pCodec == NULL)  
  71.     {  
  72.         printf("Unsupported codec");  
  73.         return -1;  
  74.     }  
  75.     printf("video size : width=%d height=%d \n", pCodecCtx->width,  
  76.            pCodecCtx->height);  
  77.     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)  
  78.     {  
  79.         printf("Unable to open codec");  
  80.         return -1;  
  81.     }  
  82.     printf("initial successfully");  
  83.     return 0;  
  84. }  
  85.   
  86. int FFmpeg::h264Decodec()  
  87. {  
  88.     int frameFinished=0;  
  89.     while (av_read_frame(pFormatCtx, &packet) >= 0)  
  90.     {  
  91.         if(packet.stream_index==videoStream)  
  92.         {  
  93.             avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);  
  94.             if (frameFinished)  
  95.             {  
  96.                 printf("***************ffmpeg decodec*******************\n");  
  97.                 mutex.lock();  
  98.                 int rs = sws_scale(pSwsCtx, (const uint8_t* const *) pFrame->data,  
  99.                                    pFrame->linesize, 0,  
  100.                                    height, picture.data, picture.linesize);  
  101.                 mutex.unlock();  
  102.                 if (rs == -1)  
  103.                 {  
  104.                     printf("__________Can open to change to des imag_____________e\n");  
  105.                     return -1;  
  106.                 }  
  107.             }  
  108.         }  
  109.     }  
  110.     return 1;  
  111.   
  112. }  
[cpp] view plain copy
  1.   
[cpp] view plain copy
  1.   
[cpp] view plain copy
  1.   
[cpp] view plain copy
  1.   


FFmpeg这个类主要是对ffmpeg的一些操作,里面主要是有两个函数,一个是initial(QString & url)。其中的参数是我们用户的输入url,类型是QString。这里建议是传递QString不要传递char * 字符,很容易出现指针异常。initial主要功能就是初始化AVFormatCtx,猜想主要是利用了sps,pps初始化了。获取了图像的尺寸我们就开始初始化我,我们要转化的RGB AVPicture了。也就是我们解码后的数据通过swscale进行图像格式转化成RGB存储在picture对面里面。h264Decodec()这个函数就是我们的解码函数,里面就是不断从网络中读取packet,如果是一个视频数据我们就开始解码,再然后就是图片格式转换。当然,这里面我们采取了多线程,一个是主线程也就是UI线程,一个是我们ffmpeg解码线程(其中包括了网络数据获取和解码以及图片格式转换)。所了我们这里面对picture数据是多线程共享的,所以了我加了一个安全锁。这样做就是避免图像刷性不完全。
[cpp] view plain copy
  1. <span style="font-family: Arial, Helvetica, sans-serif;"></span><pre code_snippet_id="215818" snippet_file_name="blog_20140303_4_771610" name="code" class="cpp">#include "video.h"  
  2. #include "ui_video.h"  
  3. #include <QPainter>  
  4. Video::Video(QWidget *parent) :  
  5.     QMainWindow(parent),  
  6.     ui(new Ui::Video)  
  7. {  
  8.     ui->setupUi(this);  
  9.     ffmpeg=NULL;  
  10. }  
  11.   
  12. Video::~Video()  
  13. {  
  14.     delete ui;  
  15. }  
  16. void Video::setFFmpeg(FFmpeg *ff)  
  17. {  
  18.     ffmpeg=ff;  
  19. }  
  20.   
  21. void Video::paintEvent(QPaintEvent *)  
  22. {  
  23.     if(ffmpeg->picture.data!=NULL)  
  24.     {  
  25.      QPainter painter(this);  
  26.     if(ffmpeg->mutex.tryLock(1000))  
  27.     {  
  28.   
  29.         QImage image=QImage(ffmpeg->picture.data[0],ffmpeg->width,ffmpeg->height,QImage::Format_RGB888);  
  30.         QPixmap  pix =  QPixmap::fromImage(image);  
  31.         painter.drawPixmap(0, 0, 640, 480, pix);  
  32.         update();  
  33.         ffmpeg->mutex.unlock();  
  34.     }  
  35.     }  
  36. }  
  37. </pre><br>  
  38. 这个是就是我们用来现实的video的,主要函数是一个回调函数<span style="font-family:Arial,Helvetica,sans-serif">paintEvent函数。里面我们是用了ffmpeg的picture来初始化Qimage,然后显示上去。关于qt二维绘图我这里就不多了,给的资料已经很详细了。</span><pre code_snippet_id="215818" snippet_file_name="blog_20140303_6_1404464" name="code" class="cpp"><span style="font-family: Arial, Helvetica, sans-serif;"></span><pre code_snippet_id="215818" snippet_file_name="blog_20140303_6_1404464" name="code" class="cpp">#include "login.h"  
  39. #include <QApplication>  
  40. #include <QString>  
  41. #include <stdio.h>  
  42. #include "FFmpeg.h"  
  43. #include <qthread.h>  
  44. #include "video.h"  
  45. #include <unistd.h>  
  46.   
  47.   
  48. /** 
  49.  * @brief The RtspThread class 
  50.  * Receive Thread 
  51.  */  
  52. class RtspThread : public QThread  
  53. {  
  54. public :  
  55.     void run();  
  56.     void setRtspURL(QString url);  
  57.     void setFFmpeg(FFmpeg * ff);  
  58. private:  
  59.     QString  rtspURL;// user input url  
  60.     FFmpeg * ffmpeg;//mian thread and rtspthread use the some ffmpeg object  
  61. };  
  62.   
  63. int main(int argc, char *argv[])  
  64. {  
  65.     QApplication a(argc, argv);  
  66.     Login login;// login Doialog  
  67.     Video video;//mainwindow for display video  
  68.     FFmpeg * ffmpeg;//global data for ffmpeg event  
  69.     ffmpeg=new FFmpeg();  
  70.     video.setFFmpeg(ffmpeg);  
  71.     RtspThread rtspthread;  
  72.     login.show();  
  73.   
  74.     if(login.exec()==QDialog::Accepted)  
  75.     {  
  76.         printf("%s\n",login.getRtspURL().toStdString().c_str());  
  77.         rtspthread.setRtspURL(login.getRtspURL());  
  78.         rtspthread.setFFmpeg(ffmpeg);  
  79.         rtspthread.start();  
  80.         video.show();  
  81.         return a.exec();  
  82.     }else  
  83.     {  
  84.         return 0;  
  85.     }  
  86.   
  87. }  
  88.   
  89. void  RtspThread::run()  
  90. {  
  91.     ffmpeg->initial(rtspURL);  
  92.     ffmpeg->h264Decodec();  
  93. }  
  94.   
  95. void RtspThread::setRtspURL(QString url)  
  96. {  
  97.     rtspURL=url;  
  98. }  
  99. void RtspThread::setFFmpeg(FFmpeg * ff)  
  100. {  
  101.     ffmpeg=ff;  
  102. }  
  103. </pre><br>  
  104. <p>这个就是我们程序的主函数。采用了多线方法实现的。使用了多窗口来获取用户输入。</p>  
  105. <pre></pre>  
  106. <pre></pre>  
  107. <pre></pre>  
  108. <pre></pre>  
  109. <pre></pre>  
  110. <pre></pre>  
  111.      
  112. </pre>