直播系统的组成部分一:音视频采集—视频采集部分
文章分为以下几个部分:
1.多媒体设备交互类库
2. 采集摄像头画面
3.采集屏幕画面
多媒体设备交互类库
在ffmpeg中有一个类库:Libavdevice,使用这个类库可以读取多媒体设备的数据,或者输出数据到多媒体设备。我们进行视频采集所用到的摄像头或者截屏,都需要用到这个库的一些函数进行操作。
在使用Libavdevice类库的时候,首先需要在代码中:
#include "libavdevice/avdevice.h"
然后在做任何逻辑操作之前先注册Libavdevice:
avdevice_register_all();
如果不注册,控制多媒体设备的那些函数就无法成功使用。
采集摄像头画面
1.新建一个C++项目,ffmpeg的环境搭建参考之前的文章:
http://www.lindasoft.com/view/article/details?articleId=638
2.查看摄像头名称
可以通过如下代码进行查看
//查看所有设备
void show_dshow_device()
{
avdevice_register_all();
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=============\n");
avformat_open_input(&pFormatCtx,"video=dummy",iformat,&options);
printf("========================");
}
运行结果:
能够看到我的摄像头名称为“Lenovo EasyCamera”,这个名称接下来我们会用到。
查看设备名称也可以通过运行ffmpeg命令进行查看:
ffmpeg -list_devices true -f dshow -i dummy
3.代码示例
#include <stdio.h>
//#include <QImage>
#include <QString>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
}
//捕获摄像头画面
void catchCamera()
{
AVFormatContext *pFormatCtx;//包含了封装格式的数据。
AVCodecContext *pCodecCtx;//码流数据的解码方式相关数据。
AVCodec *pCodec;//AVCodecContext中所包含的解码器信息。
AVFrame *pFrame, *pFrameRGB;//存储压缩编码数据相关信息的结构体。
AVPacket *packet;//解码后的数据
uint8_t *out_buffer;
AVInputFormat *ifmt;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes;
int ret, got_picture;
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
avdevice_register_all();
printf("aaaaaa\n");
//打开摄像头
pFormatCtx = avformat_alloc_context();
ifmt=av_find_input_format("dshow");
avformat_open_input(&pFormatCtx,"video=Lenovo EasyCamera",ifmt,NULL) ; //这个地方用到摄像头名称
//获取视频信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("Could't find stream infomation.\n");
return;
}
videoStream = -1;
///循环查找视频中包含的流信息,直到找到视频类型的流
///便将其记录下来 保存到videoStream变量中
///这里我们现在只处理视频流 音频流先不管他
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
}
///如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
printf("Didn't find a video stream.\n");
return;
}
///查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
printf("Codec not found.\n");
return;
}
///打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\n");
return;
}
else
{
printf("打开解码器\n");
}
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据
int index = 0;
printf("cccccc\n");
///我们就取10张图像
for(int i=0;i<10;i++)
{
if(av_read_frame(pFormatCtx, packet) < 0)
{
printf("no frame\n");
//break;
}
if(packet->stream_index==videoStream)
{
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if(got_picture)
{
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
//QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
//tmpImg.save(QString::number(i), "JPG", 100);
printf("catch %d\n",i);
}
}
av_free_packet(packet);
}
av_free(out_buffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}
int main()
{
//show_dshow_device();
catchCamera();
//catchDesktop();
return 0;
}
采集屏幕画面
采集屏幕画面和采集摄像头画面非常相似,只是把设置摄像头参数—打开摄像头这之间的步骤换成设置截屏参数—打开截屏。
下面是代码,可以对比一下:
//捕获桌面画面
void catchDesktop()
{
AVFormatContext *pFormatCtx;//包含了封装格式的数据。
AVCodecContext *pCodecCtx;//码流数据的解码方式相关数据。
AVCodec *pCodec;//AVCodecContext中所包含的解码器信息。
AVFrame *pFrame, *pFrameRGB;//存储压缩编码数据相关信息的结构体。
AVPacket *packet;//解码后的数据
uint8_t *out_buffer;
AVInputFormat *ifmt;
AVDictionary* options = NULL;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes;
int ret, got_picture;
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
avdevice_register_all();
show_dshow_device();
printf("aaaaaa\n");
//截屏
pFormatCtx = avformat_alloc_context();
//Set some options
//grabbing frame rate
av_dict_set(&options,"framerate","5",0);
//The distance from the left edge of the screen or desktop
av_dict_set(&options,"offset_x","20",0);
//The distance from the top edge of the screen or desktop
av_dict_set(&options,"offset_y","40",0);
//Video frame size. The default is to capture the full screen
av_dict_set(&options,"video_size","640x480",0);
ifmt=av_find_input_format("gdigrab");
if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){
printf("Couldn't open input stream.");
return;
}
printf("bbbbbbbbbb\n");
//获取视频信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("Could't find stream infomation.\n");
return;
}
videoStream = -1;
///循环查找视频中包含的流信息,直到找到视频类型的流
///便将其记录下来 保存到videoStream变量中
///这里我们现在只处理视频流 音频流先不管他
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
}
///如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
printf("Didn't find a video stream.\n");
return;
}
///查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
printf("Codec not found.\n");
return;
}
///打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\n");
return;
}
else
{
printf("打开解码器\n");
}
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据
int index = 0;
printf("cccccc\n");
///我们就取10张图像
for(int i=0;i<10;i++)
{
if(av_read_frame(pFormatCtx, packet) < 0)
{
printf("no frame\n");
//break;
}
if(packet->stream_index==videoStream)
{
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if(got_picture)
{
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
//QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
//tmpImg.save(QString::number(i), "JPG", 10);
printf("catch %d\n",i);
}
}
av_free_packet(packet);
}
av_free(out_buffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}