使用FFmpeg+SDL打开Mac摄像头
Posted 人生如梦91
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用FFmpeg+SDL打开Mac摄像头相关的知识,希望对你有一定的参考价值。
最近由于项目涉及到音视频方面的一点东西,所以研究了一波FFmpeg,其实我对这方面是非常有兴趣的,但是由于自己不是计算机专业出身,很多其他的东西都不了解,所以只能叹息心有余而力不足。FFmpeg是一个开源的东西,几乎没有文档,幸运的是有雷神为我们铺下了一条学习的道路,我也是跟着这条道路在走下去。可惜的是2016年7月雷神就已离我们而去,我们虽然年纪相仿,但是差距实在是大,我也很膜拜这位年轻的技术大神。写本文的目的有两个,一是缅怀雷霄骅博士,感谢他为我们做出的巨大贡献,二是我在学习的时候使用的是Mac电脑,却没发现有一篇讲述打开Mac摄像头的代码,几乎都是使用的命令行打开,于是我根据雷神的代码,加以修改,邯郸学步,写了这个打开摄像头的程序,权当学习。本人也将继续学习下去。
列出设备
写这个程序的第一步,当然是要知道机器有什么设备,查询设备的代码如下:
调用该函数显示的结果如下,其中”avfoundation”是指定的表示Mac/ios端的输入设备
可以看到,在我的机器上,有两个视频设备及一个音频设备,分别是
- [0] FaceTime HD Camera (高清摄像头)
- [1] Capture screen 0 (屏幕录制)
- [0] Built-in Microphone (内建麦克风)
我们在编译安装FFmpeg之后,会拿到一个FFmpeg命令,上述代码,就相当于一条FFmpeg命令:
打开设备
打开设备和查看设备的代码几乎一样,如下图所示:
其实跑起来之后会发现,根本无法打开摄像头,查看终端,发现会出现一些提示,我们需要为Mac的摄像头设置一些参数,如下所示:
设置参数之后的代码如下:
区别就在于红色线框标出的地方,根据上面的提示我们知道,我们必须为摄像头设置framerate参数,那么pixel_format的设置自然也是一样的,当你设置了framerate,但没有设置pixel_fromat就会出现类似的提示,我们这里选择uyvy422格式。同时使用video_size 设置视频的宽高,否则默认宽高是320x240, 中间的x号,其实是个英文字符小写的x。调用此函数,我们看到摄像头旁边的绿灯亮了起来,说明摄像头已经被成功打开。
查询流信息、解码器
接下来要做的,就是查询流信息了和对应的解码器了,代码如下所示:
首先,调用avformat_find_stream_info查询流信息,其中nb_streams表示流的个数,如果codec_type 是 AVMEDIA_TYPE_VIDEO,说明我们已经找到视频流,保留索引。
然后,通过codec_id找到对应的解码器,分配AVCodecContext,并使用avcodec_parameters_to_context方法设置对应的参数。之后调用avcodec_open2打开解码器。
分配对应数据结构
首先,我们分配一个AVPacket,这个结构是用来接收未解码的视频数据,同时分配一个AVFrame用于接收解码后的数据,也许还注意到有一个frame_yuv,因为摄像头的数据是UYVY422的格式的,但是一般的视频格式是YUV420格式,所以我们使用一个frame_yuv来接收转换后的数据,同时初始化一个SwsContext用于将数据格式从AV_PIX_FMT_UYVY422转化成AV_PIX_FMT_YUV420P。并调用av_image_fill_arrays来填充缓冲区
初始化SDL
我们初始化SDL用于显示数据,并创建窗口、渲染器和纹理,代码如下:
其中,SDL也支持UYVY422格式的数据,我们可以将格式修改成SDL_PIXELFORMAT_UYVY,这样,我们不需要转换数据,便可以直接显示。之后调用SDL_CreateThread创建线程,线程的处理方法如下:
自定义了两个消息,一个SDL_EVENT_INTERFACE_FRESH用于刷新数据,一个SDL_EVENT_QUIT用于退出SDL。我们每隔40 ms发送一个刷新数据事件,然后在主线程中使用SDL_WaitEvent等待事件发生。然后读取数据帧,解码,填充即可。
读取数据,解码填充
当一个SDL_EVENT_INTERFACE_FRESH事件到来的时候,我们就可以去刷新数据,首先使用av_read_frame读取一帧数据,然后使用avcodec_send_packet将未解码的数据发送给解码器进行解码,再使用avcodec_receive_frame从解码器接收解码后的数据,并填充到SDL中,代码如下所示:
如果我们最终使用YUV420格式,则需要使用sws_scale将数据从UYVY422转换到YUV420格式,然后填充,否则,我们可以直接填充,
SDL_UpdateTexture(sdlTexture, &sdlRect, frame->data[0], frame->linesize[0]);
而不再使用SDL_UpdateYUVTexture。同时使用fwrite将YUV数据写入到文件。
程序退出
我们还定义了一个SDL_EVENT_QUIT消息,当收到这个消息的时候,我们就退出循环,最后的代码如下:
当键盘按下的时候,我们使用SDL_GetKeyboardState拿到按下的键,如果按下的是Q键,则设置thread_exit标志为1, 通知子线程退出。
演示
最终的结果如下所示:
生成的YUV文件,我们使用YUVPlayer查看,如下图所示:
本人的长相确实有点丑,这点要承认,所以就不露脸来吓唬看过本文的网友了。如果将avformat_open_input中的”0”替换成”1”,即可录制屏幕,如下图所示:
最后再次感谢雷神,如果没有他,我甚至很多人都不知道FFmpeg应该怎么入门。我也会继续学习下去。当然毕竟我不是这个专业的,学FFmpeg只是为了项目罢了,本人纯属初学,如有错漏,希望批评指正。最后附上雷神博客地址:
雷神博客地址
以上是关于使用FFmpeg+SDL打开Mac摄像头的主要内容,如果未能解决你的问题,请参考以下文章
mac上编译雷神的《最简单的基于FFMPEG+SDL的视频播放器-最终版》代码