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 下载后, ...
随机推荐
- HttpRunner自动化框架学习笔记
一.简单介绍 HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试.性能测试.线上监控.持续集成等多种测试需求. 支持p ...
- 性能测试之七--jdbc
jdbs用任意协议打开都行,具体脚本见下 在vuser_init里面 #include "Ptt_Mysql.h" vuser_init() { lr_load_dll (&quo ...
- 国外12家值得注意的SD-WAN厂商
国外12家值得注意的SD-WAN厂商 来源 https://www.sdnlab.com/18611.html 在网络行业,围绕软件定义网络的炒作似乎已经殆尽,但业界普遍对软件定义广域网(SD-WAN ...
- 刷题总结——xor(ssoj)
题目: 题目背景 OURCE:NOIP2015-SHY-7 题目描述 求一棵带边权的树的一条最大 Xor 路径的值.这里的“路径”不一定从根到叶子结点,中间一段路径只要满足条件也可以. 输入格式 第一 ...
- element-ui select组件使用需要注意的
当在使用select组件的时候,要注意 <el-select v-model="scope.row.state" @change="editDriftStatus& ...
- uva 11468 AC自动机+概率DP
#include<cstdio> #include<cstring> #include<queue> #include<cstdio> #include ...
- LOOPS(hdu 3853)
题意:迷宫是一个R*C的布局,每个格子中给出停留在原地,往右走一个,往下走一格的概率,起点在(1,1),终点在(R,C),每走一格消耗两点能量,求出最后所需要的能量期望 /* 刚开始以为这就是个**题 ...
- FZOJ 2102 Solve equation
...
- 【POJ2104】K-th Number(主席树)
题意:有n个数组成的序列,要求维护数据结构支持在线的下列两种操作: 1:单点修改,将第x个数修改成y 2:区间查询,询问从第x个数到第y个之间第K大的数 n<=100000,a[i]<=1 ...
- ADO:DataSet存入缓存Cache中并使用
原文发布时间为:2008-08-01 -- 来源于本人的百度文章 [由搬家工具导入] using System;using System.Data;using System.Configuration ...