上一篇是将H264流封装到MP4容器中,本篇介绍一个最常用的捕获原始数据的方法:从摄像头获取数据。

因为本人已经放弃windows操作系统,所以使用linux来获取摄像头并编码为H264文件保存。

linux下使用video4linux2作为设备来定义统一的接口,先看一下摄像头数据信息:

$ ffprobe /dev/video0
...
Input #0, video4linux2,v4l2, from '/dev/video0':Duration: N/A, start: 4232.341681, bitrate: 110592 kb/sStream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 640x360, 110592 kb/s, 30 fps, 30 tbr, 1000k tbn, 1000k tbc

摄像头原始数据为YVYU422格式,而我们一般使用的是YUV420P。所以我们编码之前需要将其解码为YUV420P(当然也可以不解码,因为视频播放的时候也会解码,这里只是为了入门教学)。

那么步骤就是读取摄像头YUYV422数据->解码为YUV420P->编码为H264流->保存文件。

先看一下流程:

先将打开和保存的步骤写好,然后读取每一帧,读到一帧后解码,然后送入编码器,保存文件。

先看一下

打开摄像头部分

首先打开摄像头

avdevice_register_all();AVInputFormat *inFmt = av_find_input_format("v4l2");
if(avformat_open_input(&inFmtCtx,"/dev/video0",inFmt,NULL)<0){printf("Cannot open camera.\n");return -1;
}

先注册所有的设备,然后打开v4l2(video4linux2)用于读取摄像头数据,接下来的步骤和之前的一样

解码部分代码如下:

avdevice_register_all();/解码器部分//
AVFormatContext *inFmtCtx = avformat_alloc_context();
AVCodecContext  *inCodecCtx = NULL;
AVCodec         *inCodec =NULL;
AVPacket        *inPkt =av_packet_alloc();
AVFrame         *srcFrame =av_frame_alloc();
AVFrame         *yuvFrame =av_frame_alloc();struct SwsContext *img_ctx = NULL;int inVideoStreamIndex = -1;//打开摄像头
AVInputFormat *inFmt = av_find_input_format("v4l2");
if(avformat_open_input(&inFmtCtx,"/dev/video0",inFmt,NULL)<0){printf("Cannot open camera.\n");return -1;
}if(avformat_find_stream_info(inFmtCtx,NULL)<0){printf("Cannot find any stream in file.\n");return -1;
}for(size_t i=0;i<inFmtCtx->nb_streams;i++){if(inFmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){inVideoStreamIndex=i;break;}
}
if(inVideoStreamIndex==-1){printf("Cannot find video stream in file.\n");return -1;
}AVCodecParameters *inVideoCodecPara = inFmtCtx->streams[inVideoStreamIndex]->codecpar;
if(!(inCodec=avcodec_find_decoder(inVideoCodecPara->codec_id))){printf("Cannot find valid video decoder.\n");return -1;
}
if(!(inCodecCtx = avcodec_alloc_context3(inCodec))){printf("Cannot alloc valid decode codec context.\n");return -1;
}
if(avcodec_parameters_to_context(inCodecCtx,inVideoCodecPara)<0){printf("Cannot initialize parameters.\n");return -1;
}if(avcodec_open2(inCodecCtx,inCodec,NULL)<0){printf("Cannot open codec.\n");return -1;
}img_ctx = sws_getContext(inCodecCtx->width,inCodecCtx->height,inCodecCtx->pix_fmt,inCodecCtx->width,inCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);int numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,inCodecCtx->width,inCodecCtx->height,1);
uint8_t* out_buffer = (unsigned char*)av_malloc(numBytes*sizeof(unsigned char));ret = av_image_fill_arrays(yuvFrame->data,yuvFrame->linesize,out_buffer,AV_PIX_FMT_YUV420P,inCodecCtx->width,inCodecCtx->height,1);
if(ret<0){printf("Fill arrays failed.\n");return -1;
}
//解码器部分结束/

就是普通的打开编码器流程,此部分代码和FFmpeg4入门5:解码视频流过程部分一样。

打开保存文件部分

//编码器部分开始/
const char* outFile = "result.h264";AVOutputFormat *outFmt = NULL;
AVCodecContext *outCodecCtx=NULL;
AVCodec        *outCodec = NULL;
AVStream *outVStream     = NULL;AVPacket *outPkt = av_packet_alloc();//打开输出文件,并填充fmtCtx数据
AVFormatContext *outFmtCtx = avformat_alloc_context();
if(avformat_alloc_output_context2(&outFmtCtx,NULL,NULL,outFile)<0){printf("Cannot alloc output file context.\n");return -1;
}
outFmt = outFmtCtx->oformat;//打开输出文件
if(avio_open(&outFmtCtx->pb,outFile,AVIO_FLAG_READ_WRITE)<0){printf("output file open failed.\n");return -1;
}//创建h264视频流,并设置参数
outVStream = avformat_new_stream(outFmtCtx,outCodec);
if(outVStream==nullptr){printf("create new video stream fialed.\n");return -1;
}
outVStream->time_base.den=30;
outVStream->time_base.num=1;//编码参数相关
AVCodecParameters *outCodecPara = outFmtCtx->streams[outVStream->index]->codecpar;
outCodecPara->codec_type=AVMEDIA_TYPE_VIDEO;
outCodecPara->codec_id = outFmt->video_codec;
outCodecPara->width = 480;
outCodecPara->height = 360;
outCodecPara->bit_rate = 110000;//查找编码器
outCodec = avcodec_find_encoder(outFmt->video_codec);
if(outCodec==NULL){printf("Cannot find any encoder.\n");return -1;
}//设置编码器内容
outCodecCtx = avcodec_alloc_context3(outCodec);
avcodec_parameters_to_context(outCodecCtx,outCodecPara);
if(outCodecCtx==NULL){printf("Cannot alloc output codec content.\n");return -1;
}
outCodecCtx->codec_id = outFmt->video_codec;
outCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
outCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
outCodecCtx->width = inCodecCtx->width;
outCodecCtx->height = inCodecCtx->height;
outCodecCtx->time_base.num=1;
outCodecCtx->time_base.den=30;
outCodecCtx->bit_rate=110000;
outCodecCtx->gop_size=10;if(outCodecCtx->codec_id==AV_CODEC_ID_H264){outCodecCtx->qmin=10;outCodecCtx->qmax=51;outCodecCtx->qcompress=(float)0.6;
}else if(outCodecCtx->codec_id==AV_CODEC_ID_MPEG2VIDEO){outCodecCtx->max_b_frames=2;
}else if(outCodecCtx->codec_id==AV_CODEC_ID_MPEG1VIDEO){outCodecCtx->mb_decision=2;
}//打开编码器
if(avcodec_open2(outCodecCtx,outCodec,NULL)<0){printf("Open encoder failed.\n");return -1;
}
///编码器部分结束

此部分代码和FFmpeg4入门12:本地yuv文件编码为h264一样。

编解码部分

先将数据从原始的YUYV422转换为YUV420P,然后再将YUV420P编码为H264。

///编解码部分//
yuvFrame->format = outCodecCtx->pix_fmt;
yuvFrame->width = outCodecCtx->width;
yuvFrame->height = outCodecCtx->height;ret = avformat_write_header(outFmtCtx,NULL);int count = 0;
while(av_read_frame(inFmtCtx,inPkt)>=0 && count<50){if(inPkt->stream_index == inVideoStreamIndex){if(avcodec_send_packet(inCodecCtx,inPkt)>=0){while((ret=avcodec_receive_frame(inCodecCtx,srcFrame))>=0){if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return -1;else if (ret < 0) {fprintf(stderr, "Error during decoding\n");exit(1);}sws_scale(img_ctx,srcFrame->data,srcFrame->linesize,0,inCodecCtx->height,yuvFrame->data,yuvFrame->linesize);yuvFrame->pts=srcFrame->pts;//encodeif(avcodec_send_frame(outCodecCtx,yuvFrame)>=0){if(avcodec_receive_packet(outCodecCtx,outPkt)>=0){printf("encode one frame.\n");++count;outPkt->stream_index = outVStream->index;av_packet_rescale_ts(outPkt,outCodecCtx->time_base,outVStream->time_base);outPkt->pos=-1;av_interleaved_write_frame(outFmtCtx,outPkt);av_packet_unref(outPkt);}}usleep(1000*24);}}av_packet_unref(inPkt);}
}

就是先解码然后再编码,将两者结合起来。

结果

编译运行程序,输出为:

$ ./camera2h264
[libx264 @ 0x5565f9304cc0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x5565f9304cc0] profile High, level 3.0, 4:2:0, 8-bit
encode one frame.
...
encode one frame.
Flushing stream #0 encoder
success encoder 1 frame.
...
success encoder 1 frame.
[libx264 @ 0x5565f9304cc0] frame I:8     Avg QP:15.79  size: 46078
[libx264 @ 0x5565f9304cc0] frame P:22    Avg QP:13.08  size: 49616
[libx264 @ 0x5565f9304cc0] frame B:45    Avg QP:14.14  size: 41955
[libx264 @ 0x5565f9304cc0] consecutive B-frames: 20.0%  0.0%  0.0% 80.0%
[libx264 @ 0x5565f9304cc0] mb I  I16..4:  7.9% 60.7% 31.4%
[libx264 @ 0x5565f9304cc0] mb P  I16..4:  2.7% 31.7% 12.3%  P16..4: 15.5% 18.6% 14.4%  0.0%  0.0%    skip: 4.9%
[libx264 @ 0x5565f9304cc0] mb B  I16..4:  0.7% 16.6%  7.2%  B16..8: 24.3% 16.9%  8.6%  direct:16.1%  skip: 9.5%  L0:34.1% L1:26.8% BI:39.1%
[libx264 @ 0x5565f9304cc0] final ratefactor: -39.38
[libx264 @ 0x5565f9304cc0] 8x8 transform intra:65.9% inter:39.8%
[libx264 @ 0x5565f9304cc0] coded y,uvDC,uvAC intra: 95.0% 97.3% 93.9% inter: 77.6% 84.7% 72.6%
[libx264 @ 0x5565f9304cc0] i16 v,h,dc,p:  8% 12% 13% 68%
[libx264 @ 0x5565f9304cc0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 15% 22% 23%  5%  6%  5%  9%  5% 11%
[libx264 @ 0x5565f9304cc0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 18% 22% 13%  6%  7%  6%  9%  6% 11%
[libx264 @ 0x5565f9304cc0] i8c dc,h,v,p: 72% 11% 10%  8%
[libx264 @ 0x5565f9304cc0] Weighted P-Frames: Y:22.7% UV:22.7%
[libx264 @ 0x5565f9304cc0] ref P L0: 58.8% 10.1% 18.3% 12.8%  0.1%
[libx264 @ 0x5565f9304cc0] ref B L0: 77.4% 18.8%  3.8%
[libx264 @ 0x5565f9304cc0] ref B L1: 87.4% 12.6%
[libx264 @ 0x5565f9304cc0] kb/s:0.15

然后使用ffplay检测一下结果是否正确:

当然可以参照FFmpeg4入门8:软解并使用QtWidget播放视频(YUV420P->RGB32)直接在QT界面显示摄像头数据。

完整代码在ffmpeg_Beginner中的14.video_encode_camera2h264中。

本文首发于:FFmpeg4入门14:Linux下摄像头捕获并编码为h264

FFmpeg4入门14:Linux下摄像头捕获并编码为h264相关推荐

  1. Linux 下摄像头驱动支持情况

    http://eatdrinkmanwoman.spaces.live.com/blog/cns!97719476F5BAEDA4!1336.entry http://weijb0606.blog.1 ...

  2. linux下摄像头驱动分类

    在Windows下,摄像头驱动由厂商开发并提供.但在Linux下,因商业利益有限,只有极少厂商愿意提供摄像头驱动支持.这并不妨碍Linux下摄像头的使用--广大第三方志愿者维护着大大小小的驱动.之前做 ...

  3. linux下使用ffmpeg采集摄像头数据并编码成h264文件

    本文讲述如何在linux下,使用ffmpeg采集视频数据,并编码成h264文件. 打算分成3部分讲解: 需要具备的软硬件环境 ffmpeg命令采集摄像头数据并编码成h264文件 ffmpeg代码采集摄 ...

  4. linux 捕获sigsegv信息如何生成core文件,Linux下如何捕获SIGSEGV 的发生位置

    Linux下如何捕获SIGSEGV 的发生位置 发布时间:2008-04-23 01:28:28来源:红联作者:Mwany Linux下编程(尤其是服务端程序)若由于内存越界或其他原因产生" ...

  5. linux下配置mysql默认编码utf8

    linux下配置mysql默认编码utf8 下面是需要在对应地方加入的配置 [client] default-character-set=utf8[mysqld] character-set-serv ...

  6. Linux下摄像头驱动程序spca5xx和gspca

    最近一直忙着作些小实验,头些天做的一些笔记没有来得及整理.先放在word里了,这会实在不是特别想看书,把没有来得及写上的东西补一下. 1        spca5xx和spcaview简单介绍 Lin ...

  7. Linux 下摄像头视频采集与显示

    说明: 1.本文所提及的摄像头不是zc0301p,使用的API不是V4L,显示所使用的上位机不是QT,特此说明. 2.UVC只是一个驱动,如果它能成功驱动摄像头,会在/dev目录下出现video(或v ...

  8. linux下的c 编程入门教程,Linux下的C编程入门教程.ppt

    <Linux下的C编程入门教程.ppt>由会员分享,可在线阅读,更多相关<Linux下的C编程入门教程.ppt(14页珍藏版)>请在装配图网上搜索. 1.Linux下c+编程, ...

  9. 定时任务-Quartz、Mycat简单入门、Linux下安装MySQL、Linux下安装MyCAT、Mycat的数据库分片、Mycat读写分离

    表现层:页面 后台管理系统.商城门户.搜索系统.订单系统.商品详情系统.购物车系统 中间件:dubbo 系统之间的通信,服务的统计,rpc协议远程过程调用 同步通信 服务层:实现具体的业务逻辑 商品服 ...

最新文章

  1. python商业爬虫_商业爬虫学习笔记day1
  2. 路由器与计算机IP配置,路由器设置之前 如何给电脑设置IP地址
  3. ustc小道消息20220106
  4. 指令集及流水线基本概念
  5. RocketMQ集群特点
  6. python列表使用判断_浅谈Python数据类型判断及列表脚本操作
  7. DeepFM: A Factorization-Machine based Neural Network for CTR Prediction
  8. gitlab和github一起使用
  9. 实现自动登录:Filter 实现思路和方式
  10. 人脸识别十大关键技术
  11. 运放参数的详细解释和分析-part2-如何测量输入偏置电流Ib和输入失调电流Ios
  12. latex 编译生成的 PDF 中字体有锯齿
  13. 三网手机号码归属地查询小程序源码
  14. 雾霾指数预测与城市治理(大数据平台系列)
  15. python登录qq邮箱爬邮件_如何使用Python登录邮箱发送邮件
  16. 程序员:迟到1分钟罚300,加班却视而不见,月底工资让人寒心
  17. Scratch打气球游戏 电子学会图形化编程scratch等级考试三级真题答案2019-9
  18. 图片显示上下有空白的解决办法
  19. java几种对象的区别(PO,POJO,VO,BO,DAO)
  20. 什么是H3C的BFD MAD

热门文章

  1. SDP Profile-level-id解析
  2. java学习--硬件\软件\常用快捷键\常用DOS命令
  3. C# 隐式转换关键字 implicit
  4. AndroidStudio如何快速制作.so
  5. 文件路径匹配模式 globs匹配规则
  6. 亿级流量架构演进实战 | 从零构建亿级流量API网关 01
  7. WeTest 基础操作
  8. 自己制作的MyBatis Generator 代码军刀,文后有下载链接
  9. paddle修改模型
  10. %@ Application Codebehind=Global.asax.cs Inherits=FS.JYPEIS.WeCharReserve.WebApiApplication Lan