Ffmpeg 获取USB Camera 视频流
本文讲述的案例是如何通过Ffmpeg实现从USB Camera中获取视频流并将视频流保存到MP4文件。
本文亦适用于从USB Camera 获取视频流并将视频流转发到rtmp服务的案例,二者基本的原理和流程
一样,不同的仅仅是输出上下文。
首先撇开Ffmpeg说说基本的原理,一直觉得基本的原理是最重要的,理解了基本的原理即使出现
问题也能快速定位问题。USB Camera 对操作系统来说就是一个USB 设备,应用程序如何获得设备的基本
信息又如何跟设备进行通信呢?答案是通过设备的驱动程序,当然应用程序不会直接调用设备的驱动程序,
因为没有这个权限。一般来说,应用程序会调用操作系统API间接跟设备打交道。说到这里肯定有人会说从
USB设备获取视频流岂不是很麻烦?如果每一步都是我们自己做肯定很麻烦,幸好这个世界有库存在,库
可以解决很多问题。DShow 这个库就实现了对USB Camera设备的封装,对外暴露打开,取流,关闭等接口。
本文讲的是如何通过Ffmpeg实现从USB Camera取流,是不是Ffmpeg调用了DShow封装好的接口?是的,事
实就是这样。说了半天只是从底层描述了Ffmpeg如何获跟USB Camera打交道,下面给出从取流到保存成文
件的基本流程。
图1 Ffmpeg取流保存文件基本流程
图1 展示了Ffmpeg 获取USB Camera视频流并保存到本地文件的基本流程,如果您看过我之前写的博客应该知道
大的流程基本相同,不同的是之前大多数情况下的视频源输入的是视频流地址,本案例视频源输入的是设备的名称,
具体的名称可以从设备管理器中查看。基本代码如下:
一.打开设备获取设备基本信息。需要注意的是输入的视频格式需要填写dshow,参数rtbufsize用来设置缓冲大小,
如果缓冲小了可能会出现丢帧的情况。
int OpenInput(string inputUrl)
{
inputContext = avformat_alloc_context();
lastReadPacktTime = av_gettime();
inputContext->interrupt_callback.callback = interrupt_cb;
AVInputFormat *ifmt = av_find_input_format("dshow");
AVDictionary *format_opts = nullptr;
av_dict_set_int(&format_opts, "rtbufsize", 18432000 , 0); int ret = avformat_open_input(&inputContext, inputUrl.c_str(), ifmt,&format_opts);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "Input file open input failed\n");
return ret;
}
ret = avformat_find_stream_info(inputContext,nullptr);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "Find input file stream inform failed\n");
}
else
{
av_log(NULL, AV_LOG_FATAL, "Open input file %s success\n",inputUrl.c_str());
}
return ret;
}
二.读取视频包。注意这里的视频包是”编码”以后的数据,这里的编码不同于很复杂H264算法,只是名义上编码,
读取视频包的代码如下:
shared_ptr<AVPacket> ReadPacketFromSource()
{
shared_ptr<AVPacket> packet(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p);});
av_init_packet(packet.get());
lastReadPacktTime = av_gettime();
int ret = av_read_frame(inputContext, packet.get());
if(ret >= 0)
{
return packet;
}
else
{
return nullptr;
}
}
三. 解码视频包,首先创建解码器,接着初始化解码器,最后是调用解码Ffmpeg API进行解码,具体的代码如下:
int InitDecodeContext(AVStream *inputStream)
{
auto codecId = inputStream->codec->codec_id;
auto codec = avcodec_find_decoder(codecId);
if (!codec)
{
return -1;
} int ret = avcodec_open2(inputStream->codec, codec, NULL);
return ret; }
bool Decode(AVStream* inputStream,AVPacket* packet, AVFrame *frame)
{
int gotFrame = 0;
auto hr = avcodec_decode_video2(inputStream->codec, frame, &gotFrame, packet);
if (hr >= 0 && gotFrame != 0)
{
return true;
}
return false;
}
四. 编码 将视频包解码后要再编码的,编码之前同样需要初始化编码器。编码的格式设置为H264,具体的代码如下:
int initEncoderCodec(AVStream* inputStream,AVCodecContext **encodeContext)
{
AVCodec * picCodec;
picCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
(*encodeContext) = avcodec_alloc_context3(picCodec);
(*encodeContext)->codec_id = picCodec->id;
(*encodeContext)->has_b_frames = 0;
(*encodeContext)->time_base.num = inputStream->codec->time_base.num;
(*encodeContext)->time_base.den = inputStream->codec->time_base.den;
(*encodeContext)->pix_fmt = *picCodec->pix_fmts;
(*encodeContext)->width = inputStream->codec->width;
(*encodeContext)->height =inputStream->codec->height;
(*encodeContext)->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
int ret = avcodec_open2((*encodeContext), picCodec, nullptr);
if (ret < 0)
{
std::cout<<"open video codec failed"<<endl;
return ret;
}
return 1;
} std::shared_ptr<AVPacket> Encode(AVCodecContext *encodeContext,AVFrame * frame)
{
int gotOutput = 0;
std::shared_ptr<AVPacket> pkt(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p); });
av_init_packet(pkt.get());
pkt->data = NULL;
pkt->size = 0;
int ret = avcodec_encode_video2(encodeContext, pkt.get(), frame, &gotOutput);
if (ret >= 0 && gotOutput)
{
return pkt;
}
else
{
return nullptr;
}
}
五 . 封装写文件,编码后的视频数据不能直接写入文件,需要经过封装(打包)成mp4格式。首先我们得创建输出上下文,
并为它指定视频格式。具体的代码如下:
int OpenOutput(string outUrl,AVCodecContext *encodeCodec)
{ int ret = avformat_alloc_output_context2(&outputContext, nullptr, "mp4", outUrl.c_str());
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "open output context failed\n");
goto Error;
} ret = avio_open2(&outputContext->pb, outUrl.c_str(), AVIO_FLAG_WRITE,nullptr, nullptr);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "open avio failed");
goto Error;
} for(int i = 0; i < inputContext->nb_streams; i++)
{
if(inputContext->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO)
{
continue;
}
AVStream * stream = avformat_new_stream(outputContext, encodeCodec->codec);
ret = avcodec_copy_context(stream->codec, encodeCodec);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "copy coddec context failed");
goto Error;
}
} ret = avformat_write_header(outputContext, nullptr);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "format write header failed");
goto Error;
} av_log(NULL, AV_LOG_FATAL, " Open output file success %s\n",outUrl.c_str());
return ret ;
Error:
if(outputContext)
{
for(int i = 0; i < outputContext->nb_streams; i++)
{
avcodec_close(outputContext->streams[i]->codec);
}
avformat_close_input(&outputContext);
}
return ret ;
} int WritePacket(shared_ptr<AVPacket> packet)
{
auto inputStream = inputContext->streams[packet->stream_index];
auto outputStream = outputContext->streams[packet->stream_index];
packet->pts = packet->dts = packetCount * (outputContext->streams[0]->time_base.den) /
outputContext->streams[0]->time_base.num / 30 ;
//cout <<"pts:"<<packet->pts<<endl;
packetCount++;
return av_interleaved_write_frame(outputContext, packet.get());
}
六 .调用实例
int _tmain(int argc, _TCHAR* argv[])
{ SwsScaleContext swsScaleContext;
AVFrame *videoFrame = av_frame_alloc();
AVFrame *pSwsVideoFrame = av_frame_alloc(); Init();
int ret = OpenInput("video=USB2.0 Camera"); if(ret <0) goto Error; InitDecodeContext(inputContext->streams[0]); ret = initEncoderCodec(inputContext->streams[0],&encodeContext); if(ret >= 0)
{
ret = OpenOutput("D:\\usbCamera.mp4",encodeContext);
}
if(ret <0) goto Error; swsScaleContext.SetSrcResolution(inputContext->streams[0]->codec->width, inputContext->streams[0]->codec->height); swsScaleContext.SetDstResolution(encodeContext->width,encodeContext->height);
swsScaleContext.SetFormat(inputContext->streams[0]->codec->pix_fmt, encodeContext->pix_fmt);
initSwsContext(&pSwsContext, &swsScaleContext);
initSwsFrame(pSwsVideoFrame,encodeContext->width, encodeContext->height);
int64_t startTime = av_gettime();
while(true)
{
auto packet = ReadPacketFromSource();
if(av_gettime() - startTime > 30 * 1000 * 1000)
{
break;
}
if(packet && packet->stream_index == 0)
{
if(Decode(inputContext->streams[0],packet.get(),videoFrame))
{
sws_scale(pSwsContext, (const uint8_t *const *)videoFrame->data,
videoFrame->linesize, 0, inputContext->streams[0]->codec->height, (uint8_t *const *)pSwsVideoFrame->data, pSwsVideoFrame->linesize);
auto packetEncode = Encode(encodeContext,pSwsVideoFrame);
if(packetEncode)
{
ret = WritePacket(packetEncode);
//cout <<"ret:" << ret<<endl;
} } } }
cout <<"Get Picture End "<<endl;
av_frame_free(&videoFrame);
avcodec_close(encodeContext);
av_frame_free(&pSwsVideoFrame); Error:
CloseInput();
CloseOutput(); while(true)
{
this_thread::sleep_for(chrono::seconds(100));
}
return 0;
}
如需交流,可以加QQ群1038388075,766718184,或者QQ:350197870
视频教程 播放地址: http://www.iqiyi.com/u/1426749687
视频下载地址:http://www.chungen90.com/?news_3/
Ffmpeg 获取USB Camera 视频流的更多相关文章
- FFmpeg获取DirectShow设备数据(摄像头,录屏)
这两天研究了FFmpeg获取DirectShow设备数据的方法,在此简单记录一下以作备忘.本文所述的方法主要是对应Windows平台的. 1. 列设备 ffmpeg -list_devic ...
- 【转】FFmpeg获取DirectShow设备数据(摄像头,录屏)
这两天研究了FFmpeg获取DirectShow设备数据的方法,在此简单记录一下以作备忘.本文所述的方法主要是对应Windows平台的. 1. 列设备 ffmpeg -list_devic ...
- Jetson TX1使用usb camera采集图像 (2)
该方法只启动usb摄像头 import cv2 import numpy import matplotlib.pyplot as plot class Camera: cap = cv2.VideoC ...
- android4.0 USB Camera实例(三)UVC
前面我写了两篇文章说明了zc301的实现 详细请看 http://blog.csdn.net/hclydao/article/details/21235919 以下顺便把通用的USB也写上 前面的ZC ...
- ffmpeg从USB摄像头采集一张原始图片(转)
本文讲解使用ffmpeg从USB摄像头中采集一帧数据并写入文件保存,测试平台使用全志A20平台,其他平台修改交叉工具链即可移植.开发环境使用eclipse+CDT.交叉工具链使用arm-Linux-g ...
- [转载]树莓派新版系统上使用mjpg-streamer获取USB摄像头和树莓派专用摄像头RaspiCamera图像
树莓派新版系统上使用mjpg-streamer获取USB摄像头和树莓派专用摄像头RaspiCamera图像 网上有很多关于mjpg-stream移植到树莓派的文章,大部分还是使用的sourceforg ...
- Qt 获取usb设备信息 hacking
/************************************************************************** * Qt 获取usb设备信息 hacking * ...
- C# 系统应用之通过注册表获取USB使用记录(一)
该文章是“个人电脑历史记录清除软件”项目的系统应用系列文章.前面已经讲述了如何清除IE浏览器的历史记录.获取Windows最近访问文件记录.清除回收站等功能.现在我需要完成的是删除USB设备上的U盘. ...
- ROS_Kinetic_12 ROS程序基础Eclipse_C++(三)usb camera
ROS_Kinetic_12 ROS程序基础Eclipse_C++(三)usb camera 软件包下载地址:https://github.com/bosch-ros-pkg/usb_cam 下载后, ...
随机推荐
- Set容器——HashSet及常用API
Set容器特点: ① Set容器是一个不包含重复元素的Collection,并且最多包含一个null元素,它和List容器相反,Set容器不能保证其元素的顺序; ② 最常用的两个Set接口的实 ...
- Spring c3p0连接池配置
数据库连接池 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”.预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去.我们可以通过设定连接 ...
- Welcome-to-Swift-10属性 (Properties)
属性将值跟特定的类.结构或枚举关联.存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值.计算属性可以用于类.结构体和枚举里,存储属性只能用于类和结构体. 存储属性和计算属性通常用 ...
- python自动化测试报告(excel篇)
转:https://www.jianshu.com/p/6c5d328bf390 # -*- coding: utf-8 -*- import xlsxwriter def get_format(wd ...
- 数表(bzoj 3529)
Description 有一张N×m的数表,其第i行第j列(1 < =i < =礼,1 < =j < =m)的数值为能同时整除i和j的所有自然数之和.给定a,计算数表中不大于a ...
- 【NOIP2016练习】T3 质数 (分块)
题意:共有N盏灯,标号为1到N,有M个标有不同质数的开关,开关可以控制所有标号为其标号倍数的灯,按一次开关,所有其控制的灭着的灯都点亮,所有其控制的亮着的灯将熄灭.现在,宿管可以无限的按所有开关,所有 ...
- linux shmget shmctl
shmgetint shmget(key_t key, size_t size, int flag);key: 标识符的规则size:共享存储段的字节数flag:读写的权限返回值:成功返回共享存储的i ...
- 使用 new Q_max_capacity 參數,同樣 loading 下,粗估耗電量(UI 上的 %)。
Precondition : 除了 Q_max 外,其它參數皆同. old Q_max_capacity : 1500 mAh new Q_max_capacity : 2200 mAh 有一個 lo ...
- AC日记——图灵机游戏 codevs 2292
2292 图灵机游戏 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description [Shadow 1]第二题 ...
- chartcontrol(统计坐标图形控件)
统计图形 把控件拖放到界面会弹出下图 图形共分八系列:Bar Series,Point and line Series,pie Series,Funnel Series,Area Series,Ra ...