FFMPEG Qt录屏软件开发之视频采集

前面讲解了视频播放器的开发,初步掌握了使用FFMPEG解码音视频。

现在我们就接着讲解使用FFMPEG来编码音视频,主要是实现一个录屏软件的制作。


一个录屏软件的流程基本就是:

  1. 图像采集

  2. 图像编码

  3. 将解码好的图像封装成视频


图像的采集:

FFmpeg中有一个和多媒体设备交互的类库:Libavdevice。使用这个库可以读取电脑(或者其他设备上)的多媒体设备的数据,或者输出数据到指定的多媒体设备上。


windows上libavdevice可以采集摄像头也可以采集屏幕,这里我们先讲采集摄像头。

Windows上采集摄像头基本上就是使用dshow或vfw。(dshow和vfw就是一个采集的驱动,有点类似Linux下的v4l2)



libavdevice的使用:

首先,使用libavdevice的时候需要包含其头文件:

1
#include "libavdevice/avdevice.h"

然后,在程序中需要注册libavdevice:

1
avdevice_register_all();

接下来就可以使用libavdevice的功能了。



使用FFMPEG打开视频设备的时候,总体上和打开一个视频文件是差不多的。因为系统的设备也被FFmpeg认为是一种输入的格式(即AVInputFormat)。


前面,播放器的例子中,我们使用如下的函数来打开一个视频文件:

1
2
AVFormatContext *pFormatCtx = avformat_alloc_context();  
avformat_open_input(&pFormatCtx,  "E:in.mp4" ,NULL,NULL);


而我们使用libavdevice的时候,唯一的不同在于需要首先查找用于输入的设备。在这里使用av_find_input_format()完成:

1
2
3
AVFormatContext *pFormatCtx = avformat_alloc_context();  
AVInputFormat *ifmt=av_find_input_format( "dshow" );  
avformat_open_input(&pFormatCtx, "video=e2eSoft VCam" ,ifmt,NULL) ;

上述代码首先指定了dshow设备作为输入设备,然后指定打开名字为“e2eSoft VCam”的一个设备。


首先dshow是固定的名字,是写死的,这个没有什么疑问。

那这里的“e2eSoft VCam”要如何获得呢,下面就重点讲解下:

既然FFMPEG支持采集摄像头,那么理论上他肯定有个函数可以获取摄像头的个数以及名字吧。

开头是猜到了,但万万没想到的是FFMPEG列出设备居然也是用的avformat_open_input函数。

如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
//Show Device  
void  show_dshow_device()
{  
     AVFormatContext *pFormatCtx = avformat_alloc_context();  
     AVDictionary* options = NULL;  
     av_dict_set(&options, "list_devices" , "true" ,0);  
     AVInputFormat *iformat = av_find_input_format( "dshow" );  
     printf ("Device Info=============
");  
     avformat_open_input(&pFormatCtx, "video=dummy" ,iformat,&options);  
     printf ("========================
");  
}

以上代码可以实现列出所有的设备,可惜的是,这个执行后的结果是将设备打印到终端上的,而且中文居然还是乱码的!


再仔细一看,这个函数那么多参数,估计有办法可以将结果保存到我们的变量中。

不过呢,我们这里就是个简单例子,能知道设备名就行了,手动复制进去也是没关系的。


因此,我们直接使用FFMPEG的命令行来获取设备名。

还记得前面下载的ffmpeg-2.5.2-win32-shared.7z吧。

他的bin目录下除了dll以外还有3个.exe文件

ffmpeg.exe就是集成了ffmpeg的所有功能的一个可执行文件,ffmpeg所有的功能都可以通过命令行实现。

下面就示范下使用命令行获取可用的设备。

首先打开命令行:

切换到ffmpeg.exe所在的目录(我的是E:MyProjectsVideoDevelop fmpeg-2.5.2-win32-sharedin)、

然后输入:ffmpeg -list_devices true -f dshow -i dummy  

会发现设备列出来了,且和我们刚才使用代码列出来的是一样的。

不过这里居然也是乱码。

windows的终端使用GBK编码这个毋庸置疑了,这里乱码了,只能说明ffmpeg用的不是GBK编码了(事实上他用的是UTF-8)。

因此可以将结果输出重定向到文件:

将上面的命令改成如下:

ffmpeg -list_devices true -f dshow -i dummy  2>E:/out.txt


执行完后,会在E盘下生成一个out.txt文件,打开out.txt,里面内容如下:

哦了 可以看到不乱码的设备名了。

前期我们测试的时候就用这种方法来获取设备名了。


有时候可能手头上没有摄像头,或者想试下多个设备的感觉,但是却只有一个摄像头。

怎么办呢?

这里给大家推荐一个软件:“e2eSoft VCam”,这是一个虚拟摄像头的软件,可以将视频文件当成摄像头,对做视频开发的我们还是非常有用的。

软件自行百度下载喽。


好了,回归正题,继续我们刚才打开摄像头之后的操作。

成功打开摄像头之后,进行如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
if (avformat_find_stream_info(pFormatCtx,NULL)<0)    {
         printf ("Couldn't find stream information.
");
         return  -1;
     }
 
     videoindex=-1;
 
     for (i=0; i<pFormatCtx->nb_streams; i++)
     {
         if (pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
         {
             videoindex=i;
         }
     }
 
     if (videoindex==-1)
     {
         printf ("Couldn't find a video stream.
");
         return  -1;
     }
 
     pCodecCtx=pFormatCtx->streams[videoindex]->codec;
     pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
     
     if (pCodec==NULL)
     {
         printf ("Codec not found.
");
         return  -1;
     }
 
     if (avcodec_open2(pCodecCtx, pCodec,NULL)<0)
     {
         printf ("Could not open codec.
");
         return  -1;
     }



可以看出,这个操作和之前我们操作视频的做法是完全一样的:

都是先查找流,然后查找打开解码器。

接下来也是一样使用av_read_frame来读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
     while (1)   
     {
         if (av_read_frame(pFormatCtx, packet) < 0)
         {
             break ;
         }
 
         if (packet->stream_index==videoindex)
         {
             ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
 
             ...
         }
         av_free_packet(packet);
     }


这里的操作和之前写视频播放器的时候真的是完全一样。

所以这就是使用FFMPEG的好处。


看到这里是不是觉得很奇怪,

从摄像头采集到的数据一般都是YUV或者RGB格式(这个由摄像头决定),不管是YUV还是RGB,他们都是原始的图像数据,是没有经过压缩的,可上面为什么会有解压缩的操作。

这个问题先留着,后面如果看了ffmpeg源码后再讲。总之,不知道原因在这里是没有影响的。

这里只要知道调用了解码函数之后获取到的数据就是摄像头的原始数据了,如果摄像头支持yuv422那么得到的结果就是yuv422。


从摄像头采集到的数据格式非常多:yuv444、yuv422、yuv420等等,而我们处理中使用最多的就是yuv420的数据了,因此这里解码之后直接将他转换成yuv420,并将得到的Yuv420数据写入文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
     
     FILE  *fp_yuv= fopen ( "output.yuv" , "wb" );
     
     AVFrame *pFrame,*pFrameYUV;    pFrame=av_frame_alloc();
     pFrameYUV=av_frame_alloc();
     uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
     avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
 
     struct  SwsContext *img_convert_ctx;    
     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
 
     ///我们就读取100张图像    
     for ( int  i=0;i<100;i++)
     {        
         if (av_read_frame(pFormatCtx, packet) < 0)
         {
             break ;
         }
 
         if (packet->stream_index==videoindex)
         {
             ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
 
             if (got_picture)
             {
 
sws_scale(img_convert_ctx, ( const  uint8_t*  const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
 
                 int  y_size=pCodecCtx->width*pCodecCtx->height;                
                 fwrite (pFrameYUV->data[0],1,y_size,fp_yuv);     //Y
                 fwrite (pFrameYUV->data[1],1,y_size/4,fp_yuv);   //U
                 fwrite (pFrameYUV->data[2],1,y_size/4,fp_yuv);   //V
 
             }
         }
         av_free_packet(packet);
     }





YUV420是原始的图像数据,因此上面我们保存的out.yuv文件是可以播放出来的。

播放YUV文件可以使用:YUVPlayer 。

点我去下载:http://download.csdn.net/detail/qq214517703/9637191



点击"文件>>打开"选择我们上面保存的out.yuv。


由于YUV是原始的图像数据,是不带其他任何信息的,比如图像的宽度。

因此我们需要告诉播放器我们这个YUV图像的宽和高。

宽高信息通过pCodecCtx->width和pCodecCtx->height获得,可以加个打印信息,把他打印到终端上。

我测试用的是320x240:

注:I420就是yuv420p


既然YUV420只是原始数据,那么他更不可能带有视频的帧率信息了,因此在上面也可以看到一个帧率的设置,这个就是决定一秒钟播放多少张图像。


播放过程中会发现速度变快了好多,这个是正常现象。因为帧率是我们手动指定的。



本文代码主要是参考雷大神的博客:http://blog.csdn.net/leixiaohua1020/article/details/39702113


完整工程下载地址:http://download.csdn.net/detail/qq214517703/9637226


学习音视频技术欢迎访问 http://blog.yundiantech.com  

音视频技术交流讨论欢迎加 QQ群 121376426  

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Qt是一种跨平台的GUI开发框架,可以用来开发各式各样的软件应用,包括视频应用。如果要保存rtsp视频,可以使用Qt中的QMediaPlayer和QMediaRecorder类来实现。QMediaPlayer类可以创建一个媒体播放的对象,通过设置媒体源和播放状态来播放rtsp视频。QMediaRecorder类则可以创建一个媒体录制的对象,用于将媒体流保存到本地文件系统。可以在程序运行时通过设置QMediaRecorder的参数来指定保存的文件格式、文件名、保存路径等。 具体实现方法如下: 1. 使用QMediaPlayer对象播放rtsp视频流。通过设置QMediaPlayer的媒体源为rtsp视频流地址,然后使用play()函数开始播放视频。 2. 创建QMediaRecorder对象并将QMediaPlayer对象作为媒体源。然后通过设置QMediaRecorder的参数,如输出格式、输出文件名和保存路径等,来保存rtsp视频流。 3. 在程序中添加录制按钮和停止按钮,通过按钮的clicked()信号来控制QMediaRecorder的录制和停止操作。 通过使用Qt的多媒体框架,可以非常方便地实现rtsp视频的播放和保存操作,提高程序的可用性和用户体验。 ### 回答2: Qt是一款功能强大的跨平台桌面应用程序开发工具,它支持多种编程语言和操作系统。如果想要实现在Qt应用程序中存储RTSP视频的功能,需要使用Qt中提供的网络模块和多媒体模块。 在使用Qt进行RTSP视频保存的过程中,首先需要实现对RTSP视频流的捕获和处理。可以使用Qt中的QMediaPlayer和QMediaRecorder类,通过它们提供的接口来实现视频流的捕获和保存。 其中QMediaPlayer类提供了许多用于控制媒体播放的函数,包括play()、pause()、stop()等。而QMediaRecorder类则定义了用于录制和保存媒体的函数和属性,例如setOutputLocation()、setVideoSettings()、setEncodingSettings()等。 接下来,需要将RTSP视频流保存到指定的文件夹中。可以使用Qt中的QFile类,通过其提供的open()、write()和close()等函数,将RTSP视频流保存为.mp4格式的文件。 最后,在实现RTSP视频保存的过程中,还需要考虑视频压缩和编码的问题,尤其是当视频数据较大时,会占用大量的存储空间。可以考虑使用第三方视频编码库,例如FFmpeg进行视频压缩和编码处理。 总之,使用Qt实现RTSP视频保存功能需要涉及多个模块和技术,需要仔细地分析和设计,才能得到一个稳定、高效的视频保存方案。 ### 回答3: Qt是一种跨平台的C++应用程序开发框架。RTSP是一种流媒体协议,用于实时传输音频和视频。在Qt中,我们可以使用QtMultimedia模块来处理RTSP视频。为了保存RTSP视频,我们可以使用QMediaPlayer和QMediaRecorder类。 首先,我们需要使用QMediaPlayer类来播放RTSP视频流。我们可以使用setMedia()函数将RTSP地址作为参数传递给QMediaPlayer对象。 然后,我们可以使用QMediaRecorder类来录制RTSP视频。使用setOutputLocation()函数设置录制视频的输出位置。录制完成后,我们可以使用stop()函数停止录制,使用setOutputLocation("")函数清除输出位置。 需要注意的是,QtMultimedia模块的录制功能在不同平台上的实现方式可能存在差异。在Windows平台上,QtMultimedia使用DirectShow API进行录制。在macOS和iOS平台上,QtMultimedia使用AV Foundation Framework进行录制。 总的来说,使用QtMultimedia模块处理RTSP视频和保存视频文件是非常简单的。在使用时需要注意平台差异,以确保录制功能能够正常工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值