文章目录 1. 获取摄像头的信息 2. 打开并初始化摄像头 3. 获取摄像头数据及渲染 摄像头是我们比较常用的外设,很多场景我们都会用到摄像头。

文章目录

1. 获取摄像头的信息

2. 打开并初始化摄像头

3. 获取摄像头数据及渲染

摄像头是我们比较常用的外设,很多场景我们都会用到摄像头。比如视频直播、视频监控等各个领域都会用到摄像头。摄像头图像数据的获取,方法有很多,比如可以使用Qt自带的API获取,也可以使用DirectShow、OpenCV、FFMpeg提供的API方式获取(本质上是通过DirectShow)。本篇文章主要讲述使用FFMpeg API获取摄像头的数据信息。

下面是一个简单的摄像头显示的例子的实现效果: 使用FFMpeg获取摄像头的图像数据基本分为如下步骤:

使用 av_register_all(); 和 avdevice_register_all(); 中注册全部和设备。

获取摄像头相关信息

打开摄像头

获取摄像头数据并渲染显示

1. 获取摄像头的信息

首先要获取当前设备的摄像头列表。主要是摄像头的名称,这里我们使用Qt的API获取。方法如下:

// 获取可用摄像头列表

QList cameras = QCameraInfo::availableCameras();

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);