文章目录
1. 获取摄像头的信息
2. 打开并初始化摄像头
3. 获取摄像头数据及渲染
摄像头是我们比较常用的外设,很多场景我们都会用到摄像头。比如视频直播、视频监控等各个领域都会用到摄像头。摄像头图像数据的获取,方法有很多,比如可以使用Qt自带的API获取,也可以使用DirectShow、OpenCV、FFMpeg提供的API方式获取(本质上是通过DirectShow)。本篇文章主要讲述使用FFMpeg API获取摄像头的数据信息。
下面是一个简单的摄像头显示的例子的实现效果: 使用FFMpeg获取摄像头的图像数据基本分为如下步骤:
使用 av_register_all(); 和 avdevice_register_all(); 中注册全部和设备。
获取摄像头相关信息
打开摄像头
获取摄像头数据并渲染显示
1. 获取摄像头的信息
首先要获取当前设备的摄像头列表。主要是摄像头的名称,这里我们使用Qt的API获取。方法如下:
// 获取可用摄像头列表
QList
foreach (const QCameraInfo &cameraInfo, cameras)
{
// 获取摄像头的名称
QString cameraName = cameraInfo.description();
// 添加到ComboBox中
m_pComboBox->addItem(cameraName);
}
如果想要查看摄像头的具体信息,可以使用FFMpeg命令:
ffmpeg -list_options true -f dshow -i video="BisonCam, NB Pro"
我这里摄像头的名称为 BisonCam, NB Pro 。
得到的结果如下:
2. 打开并初始化摄像头
这里直接贴了代码
// 打开摄像头
bool CameraCapture::open(const QString& deviceName)
{
m_avFrame = av_frame_alloc();
AVInputFormat *inputFormat = av_find_input_format("dshow");
AVDictionary *format_opts = nullptr;
//av_dict_set_int(&format_opts, "rtbufsize", 3041280 * 10, 0);
av_dict_set(&format_opts, "avioflags", "direct", 0);
av_dict_set(&format_opts, "video_size", "1280x720", 0);
av_dict_set(&format_opts, "framerate", "30", 0);
av_dict_set(&format_opts, "vcodec", "mjpeg", 0);
m_pFormatContent = avformat_alloc_context();
QString urlString = QString("video=") + deviceName;
// 打开输入
int result = avformat_open_input(&m_pFormatContent, urlString.toLocal8Bit().data(), inputFormat, &format_opts);
if (result < 0)
{
qDebug() << "AVFormat Open Input Error!";
return false;
}
result = avformat_find_stream_info(m_pFormatContent, nullptr);
if (result < 0)
{
qDebug() << "AVFormat Find Stream Info Error!";
return false;
}
// find Video Stream Index
int count = m_pFormatContent->nb_streams;
for (int i=0; i { if (m_pFormatContent->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { m_nVideoStreamIndex = i; break; } } if (m_nVideoStreamIndex < 0) return false; // 查找解码器 m_pCaptureContext = m_pFormatContent->streams[m_nVideoStreamIndex]->codec; AVCodec* codec = avcodec_find_decoder(m_pCaptureContext->codec_id); if (codec == nullptr) return false; // 打开解码器 if (avcodec_open2(m_pCaptureContext, codec, nullptr) != 0) return false; // 设置尺寸、格式等信息 m_pCameraData.m_nWidth = m_pCaptureContext->width; m_pCameraData.m_nHeight = m_pCaptureContext->height; AVPixelFormat format = m_pCaptureContext->pix_fmt; format = convertDeprecatedFormat(format); if (m_isUsedSwsScale) return true; m_isUsedSwsScale = false; if (format == AV_PIX_FMT_YUV420P) m_pCameraData.m_pixelFormat = CameraData::PIXFORMAT_YUV420P; else if (format == AV_PIX_FMT_YUV422P) m_pCameraData.m_pixelFormat = CameraData::PIXFORMAT_YUV422P; else if (format == AV_PIX_FMT_YUV444P) m_pCameraData.m_pixelFormat = CameraData::PIXFORMAT_YUV444P; else { m_pCameraData.m_pixelFormat = CameraData::PIXFORMAT_RGB24; m_isUsedSwsScale = true; } return true; } 这里需要注意的有下面几点: 打开摄像头之前,可以配置一些摄像头配置的参数,比如分辨率、帧率等信息。 在打开的URL中,一定要使用如下格式 video=你的摄像头名字 。否则,则会打开失败。 打开摄像头之后的操作,就是FFMpeg的常规操作了,跟打开视频文件、打开音频文件的使用方法差不多,这里就不作过多的说明了。感兴趣的可以阅读我的这篇文章:使用FFMpeg 解码音频文件 。 3. 获取摄像头数据及渲染 这里我就直接贴出代码了 // 获取一帧数据 bool CameraCapture::capture(void) { AVPacket pkt; // 获取一帧数据 int result = av_read_frame(m_pFormatContent, &pkt); if (result) return false; if (pkt.stream_index != m_nVideoStreamIndex) { av_packet_unref(&pkt); return false; } // 解码视频数据 result = avcodec_send_packet(m_pCaptureContext, &pkt); if (result) { av_packet_unref(&pkt); return false; } result = avcodec_receive_frame(m_pCaptureContext, m_avFrame); if (result) { av_packet_unref(&pkt); return false; } // 转换成RGB24 if (m_isUsedSwsScale) { // 设置RGBFrame if (m_pRGBFrame == nullptr) { m_pRGBFrame = av_frame_alloc(); m_pRGBFrame->width = m_avFrame->width; m_pRGBFrame->height = m_avFrame->height; m_pRGBFrame->linesize[0] = m_pRGBFrame->width * m_pRGBFrame->height * 3; av_image_alloc(m_pRGBFrame->data, m_pRGBFrame->linesize, m_pRGBFrame->width, m_pRGBFrame->height, AV_PIX_FMT_RGB24, 1); } // 转化为RGB24 frameToRgbImage(m_pRGBFrame, m_avFrame);