1、读取摄像头用到的类库Libavdevice:可以读取电脑(或者其他设备上)的多媒体设备的数据,或者输出数据到指定的多媒体设备上等。
这个库支持以下设备作为输入端:avfoundation、bktr、dshow(本文使用)、fbdev、gdigrab、jack、lavfi、libcdio、openal、vfwcap等
支持以下设备作为输出端:alsa、caca、decklink、fbdev、opengl、sndio、xv等
2、libavdevice使用
头文件:#include "libavdevice/avdevice.h"
注册:libavdevice:avdevice_register_all()
2.1使用libavdevice读取数据和直接打开视频文件类似。因为系统的设备也被FFmpeg认为是一种输入的格式(即AVInputFormat)。使用FFmpeg打开一个普通的视频文件如下:
AVFormatContext *formatContext = avformat_alloc_context();
avformat_open_input(&formatContext , "Warcraft3_End.avi",nullptr,nullptr);
2.2使用libavdevice,唯一的不同在于需要首先查找用于输入的设备。(av_find_input_format())
AVFormatContext *formatContext = avformat_alloc_context();
AVInputFormat *ifmt=av_find_input_format("dshow");//windows上视频的推流格式
avformat_open_input(&formatContext , "video=Integrated Camera", ifmt,NULL);
注:1、如果是指定了vfwcap设备作为输入设备,在URL中指定打开第0个摄像头设备如下
avformat_open_input(&formatContext , 0, ifmt,NULL);
2、URL的格式是"video={设备名称}",设备名称外面不能加引号,注意空格。
3、dshow的设备名称必须要提前获取
2.3可以在 我的电脑--》管理--》设备管理器 中查找本机摄像头名称也可以通过代码获取
获取摄像头数量和名称
.pro文件中添加如下:
QT += multimedia QT += multimediawidgets
//头文件QCameraInfo、QCamera
QList cameraList = QCameraInfo::availableCameras();
qDebug()<
本文实现的是读取摄像头生成yuv和rgb数据两份数据,利用解码线程类实现边解码生成rgb数据边在窗口类播放,利用生成的yuv数据进行编码生成h264文件(可播放)
1、读取摄像头:
2、摄像头数据解码生成YUV数据后再编码生成.h264文件,可播放
用eseye_u.exe 打开.h264文件如下图:
解码类cpp文件如下:
关于编码类(yuv—>h264)的博客和源码请看文末链接
fdecode::fdecode()
{
fcode = new fcode_h264;
fcode->codecInit();//编码器初始化
}
void fdecode::registerFFmpeg()
{
av_register_all();//注册所有组件
avdevice_register_all();//注册摄像头
}
void fdecode::openVideoStream(QString filename)
{
//参数1:封装格式上下文->AVFormatContext->包含了视频信息(视频格式、大小等等...)双指针定义一颗星*,
//参数2:要打开流的路径(文件名)
//AVFormatContext保存视频(视频流)相关信息的结构体
AVFormatContext * formatContent = avformat_alloc_context();
/*2、打开摄像头*/
AVInputFormat *fmt = av_find_input_format("dshow");//windows上视频的推流格式
QString cameraName = QString("video=%1").arg(filename);
int res = avformat_open_input(&formatContent,cameraName.toStdString().c_str(),fmt,nullptr);//"video=USB2.0 VGA UVC WebCam"
if(res!=0)
{
qDebug()<<"打开摄像头失败";
return ;
}
QList cameraList = QCameraInfo::availableCameras();
qDebug()<nb_streams;i++)
{
if(formatContent->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)//streams->有AVStream结构体,AVStream->codec
{
//找到视频流(只有一个)
videoType = i;//标识视频流这个类型
break;
}
}
if(videoType == -1)
{
qDebug()<<"没有找到视频流相关信息";
return;
}
//3.3根据视频流查找编码器对应的上下文对象结构体,存储编码器以及宽高格式等
AVCodecContext *codec = formatContent->streams[videoType]->codec;
/*4、有视频流,则查找对应视频流的解码器*/
AVCodec *decoder = avcodec_find_decoder(codec->codec_id);//需要解码器的id
if(decoder ==nullptr)
{
qDebug()<<"没有找到对应的解码器";
return;
}
/*5、找到解码器后打开解码器*/
//参数:1.初始化的上下文对象 2.打开的解码器 3.类似目录的东西(没有)
res = avcodec_open2(codec,decoder,nullptr);
if(res!=0)
{
qDebug()<<"解码器打开失败";
return;
}
/*6、获取到的每一帧码流(视频流)数据写到文件中,进行循环解码*/
/*6.1写到文件用FILE结构体*/
AVPacket *pkt=nullptr;//pkt这时没有指向,要我们给他分配内存空间,希望把读出来的数据放到这块内存去
pkt = (AVPacket *)malloc(sizeof(AVPacket));
//码流数据是存到buffer里面,也需要我们动态开空间(AVBufferRef *buf;)
//开空间不知道一帧的码流数据是多少?其实编解码器告诉了宽高,以此可以计算出给码流数据开多大空间
int bufSize = codec->width*codec->height;//计算一帧(图)数据的大小
av_new_packet(pkt,bufSize);
AVFrame *pictureRGB = av_frame_alloc();;//保存解码及剔除后的像素数据(做准备)
pictureRGB->width = codec->width;
pictureRGB->height = codec->height;
pictureRGB->format = codec->pix_fmt;//格式的设置
//要存解码后的像素数据到pictureRGB,那这个数据有多大呢?
//获取解码后的一帧像素数据有多大
int numByte = avpicture_get_size(AV_PIX_FMT_RGB32,codec->width,codec->height);
//开的空间用来保存像素数据的大小
uint8_t *buffer = (uint8_t *)av_malloc(numByte*sizeof(uint8_t));
//像素数据填充到AVFrame的pictureRGB里
avpicture_fill((AVPicture *)pictureRGB,buffer,AV_PIX_FMT_RGB32,codec->width,codec->height);
//因为解码之后要伸展,所以先进行转换规则的设置,转换完进入第七步解码
SwsContext *swsContent = nullptr;
swsContent = sws_getContext(codec->width,codec->height,codec->pix_fmt,
codec->width,codec->height,AV_PIX_FMT_RGB32,
SWS_BICUBIC,nullptr,nullptr,nullptr);
AVFrame *pictureYUV = av_frame_alloc();
pictureYUV->width = codec->width;
pictureYUV->height = codec->height;
pictureYUV->format = codec->pix_fmt;//格式的设置
int numByte2 = avpicture_get_size(AV_PIX_FMT_YUV420P,codec->width,codec->height);
uint8_t *bufferYUV = (uint8_t *)av_malloc(numByte2*sizeof(uint8_t));
avpicture_fill((AVPicture *)pictureYUV,bufferYUV,AV_PIX_FMT_YUV420P,codec->width,codec->height);
SwsContext *swsContentYUV = nullptr;
swsContentYUV = sws_getContext(codec->width,codec->height,codec->pix_fmt,
codec->width,codec->height,AV_PIX_FMT_YUV420P,
SWS_BICUBIC,nullptr,nullptr,nullptr);
int count = 0;
AVFrame *picture = av_frame_alloc();//保存原始RGB、YUV数据
while(av_read_frame(formatContent,pkt) >= 0)//成功读到了数据(一帧一帧读(循环):av_read_frame)
{
/*6.2AVPacket->AVStream,要判断读到的每一帧的码流数据是不是视频流*/
if(pkt->stream_index == videoType)
{
avcodec_send_packet(codec,pkt);
res = avcodec_receive_frame(codec,picture);
//把解码得到的损坏的像素数据剔除(存到pictureRGB中)
sws_scale(swsContent,(const uint8_t * const *)picture->data,picture->linesize,0,picture->height,
pictureRGB->data,pictureRGB->linesize);
sws_scale(swsContentYUV,(const uint8_t * const *)picture->data,picture->linesize,0,picture->height,
pictureYUV->data,pictureYUV->linesize);
count++;
QImage desImage = QImage((uchar*)buffer,codec->width,codec->height,
QImage::Format_RGB32,nullptr,nullptr); //RGB32
emit sendImage(desImage);//得到图片的时候触发信号
msleep(25);
fcode->codecFrame(pictureYUV);//解码得到一帧像素数据后调用编码函数(YUV-H264)
if(count ==400)//录制400帧结束循环
{
break;
}
}
//每次都存在同一块内存空间里,要清空上一次的操作
av_packet_unref(pkt);//不是free
}
qDebug()<<"保存码流数据和像素数据成功";
fcode->writeEndFrame();//写入尾巴帧
}
void fdecode::run()
{
this->fileName="USB2.0 VGA UVC WebCam";//摄像头名称
this->registerFFmpeg();//注册
this->openVideoStream(this->fileName);
}
注意点:
要进行录像多少帧就结束的判断,否则当直接关掉运行窗口,尾巴帧都没有写入,导致生成h264文件时为0kb
有关编码类(yuv—>h264),请读者参考我的博客:
https://blog.csdn.net/hml111666/article/details/122571168
演示用的eseye_u.exe破解版免安装包:
eseye_u.exe破解版免安装包-编解码文档类资源-CSDN下载
演示用的视频下载链接:
ffmpeg(h264、yuv、rgb)演示视频-C/C++文档类资源-CSDN下载
源码下载链接:
Qt基于FFmpeg读取摄像头并进行H264编码-编解码文档类资源-CSDN下载