基于FFMPEG读取摄像头图像编码为h264

2024-08-28 19:28

本文主要是介绍基于FFMPEG读取摄像头图像编码为h264,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.调用ffmpeg命令采集摄像头图像

$ ffmpeg -f v4l2 -framerate 30 -video_size 1280*720 -i /dev/video0 -c:v libx264 -preset veryfast -f h264 output.h264

  -f v4l2: 指定输入设备采用Video4Linux2框架。
  -framerate 30: 设置帧率为30。
  -video_size 1280720: 设置视频分辨率为1280720
  -i /dev/video0: 指定输入设备文件路径。
  -c:v libx264: 指定使用H.264编码。
  -preset veryfast: 选择快速编码预设。
  -f h264: 输出格式为H.264帧。
  Output.h264: 输出文件。

2 调用ffmpeg库实现摄像头采集并编码为h264

  • ffmpeg 采集摄像头图像,编码为H264格式步骤:

  1.注册设备avdevice_register_all();
  2.查找摄像头框架格式av_find_input_format(“video4Linux2”);
  3.设置摄像头参数options:图像尺寸(video_size)、帧率(framerate)、图像格式(input_format),av_dict_set();
  4.打开输入文件,获取输入上下文指针avformat_open_input();
  5.获取摄像头图像流信息avformat_find_stream_info;
  6.查找摄像头中的视频流av_find_best_stream;
  7.根据编码格式,获取解码器avcodec_find_decoder_by_name(“libx264”);
  8.分配编码器上下文指针avcodec_alloc_context3();
  9.设置图像编码参数:图像尺寸、帧率framebate、time_base、gop_size、pix_fmt,将编码器关联到AVDocodecCotext指针;
  10.创建输出文件fopen
  11.创建视频帧av_frame_alloc();
  12.设置frame参数:宽度、高度、图像格式;
  13.为frame中data和buf分配空间:av_frame_get_buffer();
  14.分配packet包,用于存放h264编码后的数据;
  15.从摄像头中读取采集的数据av_read_frame();
  17.判断是否为视频流,将packet中的yuv422数据转换为yuv420p格式,并保存到frame中;
  18.将frame中的流数据进行h264格式编码encodec_video();

  • 编码流程图如下:

在这里插入图片描述
示例代码:

#include <stdio.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavutil/imgutils.h>
#include <unistd.h>
#include <signal.h>
#define VIDEO_DEV "/dev/video0"
static int video_width;
static int video_height;
int camera_flag=0;
void YUYV422_toYuv420p(AVFrame *frame,AVPacket *pkt){
/*yuv422 存储格式为 y      u y v y u y v y u y v y u y vyuv422 每两个y公用一组UV分量,yuyv(yuv422)一个像素大小:y+1/2(u)+1/2(v)=2byteyuv420p  存储最简单,先存所以的y,再存u,最后v,yuv420p 每4个Y共用一组UV分量所以先把422所有的y存在一起,再提奇数行的u  ,偶数行舍弃。提完u后,再提v,v也是偶数行不提取。
*/int i = 0;int yuv422_length=video_width*video_height*2;//yuv422图像大小int y_index = 0;// 取出Y分量数据for (i = 0; i < yuv422_length; i += 2) {frame->data[0][y_index] = pkt->data[i];y_index++;}// copy u and vint line_start = 0;int is_u = 1;int u_index = 0;int v_index = 0;// copy u, v per line. skip a line oncefor (i = 0; i < video_height; i += 2) {line_start = i * (video_width<<1);//每一行的起始位置,相当于:video_width*2for (int j = line_start + 1; j < line_start + (video_width<<1); j += 4){frame->data[1][u_index++]=pkt->data[j];frame->data[2][v_index++]=pkt->data[j+2];}}	
}
//编码视频格式
int encodec_video(FILE *fp,AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt){int ret=0;//将数据帧传入编码器进行编码,该函数仅编码数据,并不会写入ret=avcodec_send_frame(ctx,frame);if(ret){av_log(ctx,AV_LOG_ERROR,"编码视频帧失败ret=%s\n",av_err2str(ret));return -1;}//从编码器中读取编码好的数据帧while((ret=avcodec_receive_packet(ctx,pkt))>=0){if(ret==AVERROR(EAGAIN) || ret==AVERROR_EOF)//数据帧不可用或者没有新的数据帧{av_packet_unref(pkt);//减少引用次数break;}else if(ret==AVERROR(EINVAL)){//没有正确打开编码器av_packet_unref(pkt);//减少引用次数return -1;}//将编码好的数据写入到文件fwrite(pkt->data,pkt->size,1,fp);av_packet_unref(pkt);//减少引用次数}return 0;
}//采集摄像头数据,将摄像头数据进行h264编码
//摄像头初始化
void *video_CollectImage(void *arg)
{//1.注册设备avdevice_register_all();const AVInputFormat *ifmt=NULL;//输入格式AVFormatContext *pfmtctx=NULL;//输入上下文AVDictionary *options=NULL;//其它参数const AVCodec *ocodec=NULL;AVCodecContext *icodecCtx=NULL;//解码器上下文指针AVPacket *opkt=NULL;AVFrame *iframe=NULL;FILE *fp=NULL;int ret=0;int idx=-1;//视频流下标//2.查找输入格式ifmt=av_find_input_format("video4linux2");if(ifmt==NULL){av_log(NULL,AV_LOG_ERROR,"video4linux2格式信息获取失败\n");return (void *)-1;}av_dict_set(&options,"video_size","1280*720",0);//设置图像大小av_dict_set(&options,"framerate","30",0);//帧率av_dict_set(&options,"input_format","yuv420p",0);//图像格式ret=avformat_open_input(&pfmtctx,VIDEO_DEV,ifmt,&options);if(ret<0){av_log(NULL,AV_LOG_ERROR,"打开输入文件,设置输入上下文指针失败,ret=%s\n",av_err2str(ret));return 0;}//通过读取数据包,获取流信息avformat_find_stream_info(pfmtctx,NULL);av_dump_format(pfmtctx, 0, VIDEO_DEV, 0);//3.寻找视频流idx=av_find_best_stream(pfmtctx,AVMEDIA_TYPE_VIDEO, -1,-1,NULL, 0);if(idx<0){av_log(&pfmtctx,AV_LOG_ERROR,"获取视频流失败ret=%s\n",av_err2str(idx));goto _fil;}av_log(pfmtctx,AV_LOG_INFO,"idx=%d\n",idx);video_width=pfmtctx->streams[idx]->codecpar->width;video_height=pfmtctx->streams[idx]->codecpar->height;//1.根据名字获取注册的编码器ocodec=avcodec_find_encoder_by_name("libx264");if(!ocodec){av_log(NULL, AV_LOG_ERROR, "libx264 获取编码器失败\n");goto _fil;}av_log(pfmtctx,AV_LOG_INFO,"libx264格式:%d\n",ocodec->id);//5.分配AVCodecContext上下文指针icodecCtx=avcodec_alloc_context3(ocodec);if(icodecCtx==NULL){av_log(pfmtctx,AV_LOG_ERROR,"分配上下文指针失败\n");goto _fil;}//设置图像尺寸icodecCtx->width=video_width;icodecCtx->height=video_height;icodecCtx->bit_rate=1500000;//码率icodecCtx->time_base=(AVRational){1,25};//时间基准icodecCtx->framerate=(AVRational){25,1};//帧率icodecCtx->gop_size=10;//一组图像的是数量icodecCtx->max_b_frames=2;//B帧数量icodecCtx->pix_fmt=AV_PIX_FMT_YUV420P;//图像格式if(ocodec->id==AV_CODEC_ID_H264)//编码流格式{/*设置私有属性信息int av_opt_set(void *obj, const char *name, const char *val, int search_flags);obj: 需要设置选项的对象。name: 要设置的选项名称。val: 设置的选项值。search_flags: 搜索标志,通常为0。*/av_opt_set(icodecCtx->priv_data,"preset","slow", 0);}//关联编码器上下文ret=avcodec_open2(icodecCtx,ocodec, NULL);if(ret<0){av_log(icodecCtx,AV_LOG_ERROR,"关联编码器上下文件指针失败ret=%s\n",av_err2str(ret));goto _fil;}fp=fopen("camera.h264","w+b");if(fp==NULL){av_log(icodecCtx,AV_LOG_ERROR,"文件创建失败\n");goto _fil;}//创建视频帧iframe=av_frame_alloc();if(iframe==NULL){av_log(icodecCtx,AV_LOG_ERROR,"创建视频帧frame失败\n");goto _fil;}iframe->width=video_width;iframe->height=video_height;iframe->format=icodecCtx->pix_fmt;ret=av_frame_get_buffer(iframe, 0);if(ret<0){av_log(icodecCtx,AV_LOG_ERROR,"分别frame buffer缓冲区失败,ret=%s\n",av_err2str(ret));goto _fil;}//7.创建数据包AVPacket ipkt;opkt=av_packet_alloc();if(!opkt){av_log(icodecCtx,AV_LOG_ERROR,"分配packet失败\n");goto _fil;}int i=0;av_log(NULL,AV_LOG_INFO,"开始读取数据包\n");camera_flag=1;//读取数据包while(av_read_frame(pfmtctx, &ipkt)>=0 && camera_flag==1){if(ipkt.stream_index == idx)//判断是否为视频帧{av_log(pfmtctx,AV_LOG_INFO,"pts=%ld\n",ipkt.pts);YUYV422_toYuv420p(iframe,&ipkt);//格式转换iframe->pts=av_rescale_q_rnd(ipkt.dts,pfmtctx->streams[idx]->time_base ,icodecCtx->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);av_log(pfmtctx,AV_LOG_INFO,"pts:%ld\n",iframe->pts);encodec_video(fp,icodecCtx,iframe,opkt);if(ret<0){goto _fil;}}av_packet_unref(&ipkt);//减少引用次数}encodec_video(fp,icodecCtx,iframe,opkt);fclose(fp);av_log(pfmtctx,AV_LOG_INFO,"数据采集完成\n");
_fil:if(pfmtctx){avformat_close_input(&pfmtctx);//释放上下文指针pfmtctx=NULL;}av_log(NULL,AV_LOG_INFO,"上下文指针释放成功\n");avcodec_free_context(&icodecCtx);av_frame_free(&iframe);av_packet_free(&opkt);}
void sig_work(int sig)
{if(sig==SIGINT){camera_flag=0;}
}
int main(int argc,char **argv)
{signal(SIGINT,sig_work);av_log_set_level(AV_LOG_DEBUG);video_CollectImage(NULL);
}

这篇关于基于FFMPEG读取摄像头图像编码为h264的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1115670

相关文章

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

matlab读取NC文件(含group)

matlab读取NC文件(含group): NC文件数据结构: 代码: % 打开 NetCDF 文件filename = 'your_file.nc'; % 替换为你的文件名% 使用 netcdf.open 函数打开文件ncid = netcdf.open(filename, 'NC_NOWRITE');% 查看文件中的组% 假设我们想读取名为 "group1" 的组groupName

ffmpeg面向对象-待定

1.常用对象 rtsp拉流第一步都是avformat_open_input,其入参可以看下怎么用: AVFormatContext *fmt_ctx = NULL;result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL); 其中fmt_ctx 如何分配内存的?如下 int avformat_open_input(

argodb自定义函数读取hdfs文件的注意点,避免FileSystem已关闭异常

一、问题描述 一位同学反馈,他写的argo存过中调用了一个自定义函数,函数会加载hdfs上的一个文件,但有些节点会报FileSystem closed异常,同时有时任务会成功,有时会失败。 二、问题分析 argodb的计算引擎是基于spark的定制化引擎,对于自定义函数的调用跟hive on spark的是一致的。udf要通过反射生成实例,然后迭代调用evaluate。通过代码分析,udf在

下载/保存/读取 文件,并转成流输出

最近对文件的操作又熟悉了下;现在记载下来:学习在于 坚持!!!不以细小而不为。 实现的是:文件的下载、文件的保存到SD卡、文件的读取输出String 类型、最后是文件转换成流输出;一整套够用了; 重点: 1:   操作网络要记得开线程; 2:更新网络获取的数据 切记用Handler机制; 3:注意代码的可读性(这里面只是保存到SD卡,在项目中切记要对SD卡的有无做判断,然后再获取路径!)

ROS1 + Realsense d455 固件安装+读取rostopic数据

目录 安装固件(一定要匹配)ROS1 wrapper 安装方法Realsense SDK 安装方法Realsense Firmware 安装方法 修改roslaunch配置文件,打开双目图像和IMU数据其他坑点参考链接 安装固件(一定要匹配) 如果你是使用ROS1获取realsense数据的话,一定要注意,SDK, Firmware的版本不是越新越好!!,这是因为intel已经不

Python批量读取身份证信息录入系统和重命名

前言 大家好, 如果你对自动化处理身份证图片感兴趣,可以尝试以下操作:从身份证图片中快速提取信息,填入表格并提交到网页系统。如果你无法完成这个任务,我们将在“Python自动化办公2.0”课程中详细讲解实现整个过程。 实现过程概述: 模块与功能: re 模块:用于从 OCR 识别出的文本中提取所需的信息。 日期模块:计算年龄。 pandas:处理和操作表格数据。 PaddleOCR:百度的

FFmpeg系列-视频解码后保存帧图片为ppm

在正常开发中遇到花屏时怎么处理呢?可以把解码后的数据直接保存成帧图片保存起来,然后直接看图片有没有花屏来排除是否是显示的问题,如果花屏,则代表显示无问题,如果图片中没有花屏,则可以往显示的方向去排查了。 void saveFrame(AVFrame* pFrame, int width, int height, int iFrame){FILE *pFile;char szFilename[

4-4.Andorid Camera 之简化编码模板(获取摄像头 ID、选择最优预览尺寸)

一、Camera 简化思路 在 Camera 的开发中,其实我们通常只关注打开相机、图像预览和关闭相机,其他的步骤我们不应该花费太多的精力 为此,应该提供一个工具类,它有处理相机的一些基本工具方法,包括获取摄像头 ID、选择最优预览尺寸以及打印相机参数信息 二、Camera 工具类 CameraIdResult.java public class CameraIdResult {

java读取resource/通过文件名获取文件类型

java读取resource java读取resource目录下文件的方法: 借助Guava库的Resource类 Resources.getResource("test.txt") 通过文件名获取文件类型 mongodb java