FFMPEG-摄像头采集保存YUV + 读取摄像头并编码封装保存成flv

本文介绍了如何使用FFmpeg库从摄像头捕获数据并编码为H.264,保存为本地文件。通过示例代码展示了利用DirectShow设备输入和`avformat_open_input`函数的操作,以及YUV数据的存储结构。还提到了在Windows上处理摄像头设备的一些常见问题和解决方案。
摘要由CSDN通过智能技术生成

最简单的基于FFmpeg的AVDevice例子(读取摄像头)–Libavdevice支持??设备作为输入端/输出端

FFmpeg获取DirectShow设备数据(摄像头,录屏)
①首先要ffmpeg安装到windows:static版本放到某一个目录下并添加到系统环境变量path中
  查看版本:ffmpeg-version
在这里插入图片描述
②命令:列出设备: ffmpeg -list_devices true -f dshow -i dummy
在这里插入图片描述
问题①:无法列出设备:没有安装screen-capture-recorder和virtual-audio-capturer
ffmpeg 录屏 screen capture recorder
问题②:设备名显示为中文乱码
//列表显示设备的名称很重要,输入的时候都是使用“-f dshow -i video="{设备名}"”的方式。
下面是正确显示:
[dshow @0388f5e0] DirectShow video devices
[dshow @0388f5e0] “Integrated Camera”
[dshow @0388f5e0] “screen-capture-recorder”
[dshow @0388f5e0] DirectShow audio devices
[dshow @0388f5e0] “鍐呰楹﹀厠椋?(Conexant20672 SmartAudi”
[dshow @0388f5e0] “virtual-audio-capturer”
//screen-capture-recorder和virtual-audio-capturer需要安装 Integrated Camera例如联想笔记本上的一体化摄像头

2. 获取摄像头数据(保存为本地文件或者发送实时流)

2.1. 编码为H.264,保存为本地文件

下面这条命令,实现了从摄像头读取数据并编码为H.264,最后保存成mycamera.mkv。

ffmpeg -f dshow -i video="Integrated Camera" -vcodec libx264 mycamera.mkv
最简单的基于Libavdevice的摄像头数据读取一帧YUV数据,并保存成output.yuv文件:

①普通的文件输入:
  AVFormatContext *pFormatCtx = avformat_alloc_context();
  avformat_open_input(&pFormatCtx, “test.h265”,NULL,NULL);
②利用设备输入:
  AVFormatContext *pFormatCtx = avformat_alloc_context();
  AVInputFormat *ifmt=av_find_input_format(“vfwcap”);//设备名(如vfwcap video4linux2)→确定fmt
  avformat_open_input(&pFormatCtx, 0, ifmt,NULL);//0表示在URL中指定打开第0个设备(在我自己计算机上即是摄像头设备)。
  
 在Windows平台上除了使用vfw设备作为输入设备之外,还可以使用DirectShow作为输入设备:
  AVFormatContext *pFormatCtx = avformat_alloc_context();
  AVInputFormat *ifmt=av_find_input_format(“dshow”);
  avformat_open_input(&pFormatCtx,“video=Integrated Camera”,ifmt,NULL) ;
  
  
avformat_open_input也可以不输入指定的AVInputFormat,而是缺省,通过第二个传参url自行判断

YUV420P(planar格式)在ffmpeg中存储是在struct AVFrame的data[]数组中
data[0]——-Y分量
data[1]——-U分量
data[2]——-V分量   ->故需要对data[012]依次存放才能是YUVp
linesize[]数组中保存的是对应通道的数据宽度
linesize[0]——-Y分量的宽度
linesize[1]——-U分量的宽度
linesize[2]——-V分量的宽度
注意: linesize[0]的值并不一定等于图片的宽度。我将一张1366*768的图片编码后,linesize[0]的值为1376,大概是为了内存对齐的缘故,我就没深究了。

读摄像头->pkt解码转换->目标frame->pFrameYUV->data[012]依次存放到目标文件(应该不算标准yuv文件,不可只有像素?)
#include "libavdevice/avdevice.h"
int main(int argc, char* argv[])  
{
     
//1、prepare
	AVFormatContext *pFormatCtx;  
	int             i, videoindex;  
	AVCodecContext  *pCodecCtx;  
	AVCodec         *pCodec;  
	
	av_register_all();  
	avformat_network_init();    
	avdevice_register_all();  
	
	pFormatCtx = avformat_alloc_context();  
	AVInputFormat *ifmt=av_find_input_format("video4linux2");  
	if(avformat_open_input(&pFormatCtx,"/dev/video0",ifmt,NULL)!=0)		{
   printf("Couldn't open input stream./dev/video0\n");  return -1;  }  
   
	if(avformat_find_stream_info(pFormatCtx,NULL)<0)  {
     printf("Couldn't find stream information.\n");  return -1;  }  
	
	videoindex=-1;  
	for(i=0; i<pFormatCtx->nb_streams; i++)   
		if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)  {
     videoindex=i;  break;  }  
	if(videoindex==-1)  {
     printf("Couldn't find a video stream.\n");  return -1;  }  
	
	pCodecCtx=pFormatCtx->streams[videoindex]->codec;  
	pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  //if(pCodec==NULL)  {  printf("Codec not found.\n");  return -1;  }  
	if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)  {
     printf("Could not open codec.\n");  return -1;  }  
	
	AVFrame *pFrame,*pFrameYUV;  
	pFrame=av_frame_alloc();  
	pFrameYUV=av_frame_alloc();  
	unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));  
	avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);  //关联pFrameYUV和out_buffer
	//根据获取摄像头的宽高和指定的像素格式420,分配空间
	//printf("camera width=%d height=%d \n",pCodecCtx->width, pCodecCtx->height);  
	
			
	int ret, got_picture;  
	AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));  
   
#if OUTPUT_YUV420P   
	FILE *fp_yuv=fopen("output.yuv","wb+");   //打开目标输出文件 
#endif    
   
	struct SwsContext *img_convert_ctx;  
	img_convert_ctx = sws_getContext(	pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 
										pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);   



//2、读取--解码--转换-保存【YUV3个分量依次存放pFrameYUV->data[012]】--保存成yuv图片	  
	if(av_read_frame(pFormatCtx, packet)>=0){
     
		if(packet->stream_index==videoindex){
     
			ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); 
			if(ret < 0){
   printf("Decode Error.\n");  return -1;  }  
			if(got_picture){
     
				sws_scale(	img_convert_ctx, 
							(const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
							pFrameYUV->data, pFrameYUV->linesize);  

#if OUTPUT_YUV420P    
				int y_size=pCodecCtx->width*pCodecCtx->height;      
				fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y     
				fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U    
				fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V    
#endif    
			}  
		}  
		av_free_packet(packet);    
	}  
	
//3、end	
	sws_freeContext(img_convert_ctx);  
#if OUTPUT_YUV420P   
	fclose(fp_yuv);  
#endif   
	av_free(out_buffer);  
	av_free(pFrameYUV);  
	avcodec_close(pCodecCtx);  
	avformat_close_input(&pFormatCtx);  
   
	return 0;  
}  

下面则较为严谨的版本:

//FFMPEG抓取摄像头数据保存为一张图片11yuv420.yuv(转换成)
//编译指令:	gcc usb_baocun_yuv.c -o video-frame.out -I/root/ffmpeg/include/ -L/root/ffmpeg/lib -lavcodec -lavformat -lavdevice -lavutil -lswscale -lSDL
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>

char* input_name= "video4linux2";		//没有用到啊
char* file_name = "/dev/video0";		//当做文件读取
char* out_file = "11yuv420.yuv";//使用专用软件打开时如果打开时的格式与实际不符合会出错(按照错误的方式解读)

static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;

typedef struct StreamContext {
   //准备好?个码流解码/编码所需的AVCodecContext
	AVCodecContext *dec_ctx;
	AVCodecContext *enc_ctx;
} StreamContext;

static StreamContext *stream_ctx = NULL;
const char* jpeg_file = "cuc_view_encode.jpg";
unsigned int g_stream_index = -1;			//视频流的序号

void release_frame(AVFrame *frame)
{
   
	if(frame){
   
		av_frame_unref(frame);
		av_frame_free(&frame);
	}
}

void release_packet(AVPacket *packet)
{
   
	if(packet){
   
		av_packet_unref(packet);
		av_packet_free(&packet);
	}
}

//打开输入文件并获取其中各个码流的stream_ctx[?]->dec_ctx
int open_input_file()
{
   
	int i = 0;
	int ret = -1;
	int videoindex = -1;
	ifmt_ctx = NULL;//里面有?个码流--对应stream_ctx[?]

	if((ret = avformat_open_input (&ifmt_ctx, file_name, NULL, NULL)) < 0) {
   av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");return ret;}
	//av_dump_format(ifmt_ctx,0,file_name,0);
	if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
   av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return;}
	stream_ctx = av_mallocz_array(ifmt_ctx->nb_streams, sizeof(*stream_ctx))
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值