0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

FFMPEG采集攝像頭圖像SDL渲染+MP4格式視頻編碼

嵌入式技術(shù) ? 來(lái)源:嵌入式技術(shù) ? 作者:嵌入式技術(shù) ? 2022-09-29 15:36 ? 次閱讀

FFMPEG采集攝像頭圖像SDL渲染+MP4格式視頻編碼

FFmpeg是一套可以用來(lái)記錄、轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源計(jì)算機(jī)程序。采用LGPL或GPL許可證。它提供了錄制、轉(zhuǎn)換以及流化音視頻的完整解決方案。它包含了非常先進(jìn)的音頻/視頻編解碼庫(kù)libavcodec,為了保證高可移植性和編解碼質(zhì)量,libavcodec里很多code都是從頭開發(fā)的。

1.硬件平臺(tái)

操作系統(tǒng):Ubuntu18.04
ffmpeg版本:ffmpeg4.2.5
攝像頭:電腦自帶或USB免驅(qū)攝像頭

ffmpeg采集攝像數(shù)據(jù)編碼MP4格式視頻參考:https://blog.csdn.net/xiaolong1126626497/article/details/105446774
ffmpeg采集攝像數(shù)據(jù)通過(guò)SDL渲染參考:https://blog.csdn.net/weixin_44453694/article/details/121978238

2.示例代碼

??本示例采樣三個(gè)線程實(shí)現(xiàn):
???子線程1實(shí)現(xiàn)ffmpeg編解碼器注冊(cè),設(shè)置圖像格式,圖像數(shù)據(jù)采集。
???子線程2實(shí)現(xiàn)MP4視頻格式編碼。
???主線程完成子線程創(chuàng)建,SDL庫(kù)初始化,窗口創(chuàng)建,圖像數(shù)據(jù)渲染。

2.1 完整示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define VIDEO_DEV "/dev/video0"
char file_name[40];//視頻編碼文件名
#define STREAM_FRAME_RATE 25 /*每秒25幀*/
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /*圖像格式y(tǒng)uv420p*/
#define STREAM_DURATION 60.0 /*錄制時(shí)間60.0s*/
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;//互斥鎖
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//條件變量
int width;//圖像寬
int height;//圖像高
int size;//圖像數(shù)據(jù)大小
int mp4_decode_stat=0;
unsigned char *rgb_buff=NULL;
unsigned char video_flag=1;
void *Video_CollectImage(void *arg);
void *Video_savemp4(void*arg);
typedef enum
{
	false=0,
	true,
}bool;
typedef struct OutputStream
{
	AVStream *st;
	AVCodecContext *enc;
	int64_t next_pts;/*將生成的下一幀的pts*/
	AVFrame *frame;/*保存編解碼數(shù)據(jù)*/
	AVFrame *tmp_frame;
	struct SwsContext *sws_ctx;
	struct SwrContext *swr_ctx;
}OutputStream;
typedef struct IntputDev
{
	AVCodecContext *pcodecCtx;
	AVCodec *pCodec;
	AVFormatContext *v_ifmtCtx;
	int videoindex;//視頻幀ID
	struct SwsContext *img_convert_ctx;
	AVPacket *in_packet;
	AVFrame *pFrame,*pFrameYUV;
}IntputDev;
IntputDev video_input={0};//視頻輸入流

/*添加一個(gè)輸出流*/
static void add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
{
	AVCodecContext *c;
	int i;
	/*查找編碼器*/
	*codec=avcodec_find_encoder(codec_id);
	if(*codec==NULL)
	{
		printf("Could not find encoder for ' %s' n",avcodec_get_name(codec_id));
		exit(1);
	}
	/*向媒體文件添加新流。*/
	ost->st=avformat_new_stream(oc,NULL);
	if(ost->st==NULL)
	{
		printf("Could not allocate stream n");
		exit(1);
	}
	ost->st->id=oc->nb_streams-1;
	/*分配AvcodeContext并將其字段設(shè)置為默認(rèn)值*/
	c=avcodec_alloc_context3(*codec);
	if(c==NULL)
	{
		printf("avcodec_alloc_context3 failed n");
	}
	ost->enc=c;		
	switch((*codec)->type)
	{
		case AVMEDIA_TYPE_AUDIO:
			
			break;	
		case AVMEDIA_TYPE_VIDEO:/*視頻流*/
		
			c->codec_id=codec_id;
			c->bit_rate=2500000;//比特率
			/*分辨率必須是2的倍數(shù)。*/
			c->width=width;
			c->height=height;
			/*時(shí)基:這是時(shí)間的基本單位(秒)
				其中幀時(shí)間戳被表示。對(duì)于固定fps內(nèi)容,時(shí)基應(yīng)為1/幀率,時(shí)間戳增量應(yīng)與1相同*/
			ost->st->time_base=(AVRational){1,STREAM_FRAME_RATE};
			c->time_base=ost->st->time_base;
			c->gop_size=12;/*最多每12幀發(fā)射一幀*/
			c->pix_fmt=STREAM_PIX_FMT;/*圖像格式*/
			if(c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
			{
				/*
				為了測(cè)試,我們還添加了B幀
				*/
				c->max_b_frames=2;
			}
			if(c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
			{
				/*
				需要避免使用某些系數(shù)溢出的宏塊。
				這種情況不會(huì)發(fā)生在普通視頻中,只會(huì)發(fā)生在這里
				色度平面的運(yùn)動(dòng)與亮度平面不匹配。*/
				c->mb_decision=2;
			}
			break;
		default:
			break;
	}
	/*有些格式希望流頭是分開的。*/
	if(oc->oformat->flags & AVFMT_GLOBALHEADER)
	{
		c->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;
	}
}
/*視頻輸出*/
static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
	AVFrame *picture;
	int ret;
	/*
		分配AVFrame并將其字段設(shè)置為默認(rèn)值。結(jié)果
		必須使用av_frame_free()釋放結(jié)構(gòu)。
	*/
	picture=av_frame_alloc();
	if(picture==NULL)
	{
		return  NULL;
	}
	picture->format=pix_fmt;
	picture->width=width;
	picture->height=height;
	/*為幀數(shù)據(jù)分配緩沖區(qū)*/
	ret=av_frame_get_buffer(picture,32);/*緩沖區(qū)以32位對(duì)齊*/
	if(ret<0)
	{
		printf("Could not allocate frame datan");/*無(wú)法分配幀數(shù)據(jù)*/
		exit(1);
	}
	return picture;
}

static void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
	int ret;
	AVCodecContext *c=ost->enc;
	AVDictionary *opt=NULL;
	av_dict_copy(&opt,opt_arg, 0);
	/*初始化AvcodeContext以使用給定的AVCodec*/
	ret=avcodec_open2(c, codec,&opt);
	/*釋放為AVDictionary結(jié)構(gòu)分配的所有內(nèi)存*以及所有鍵和值。*/
	av_dict_free(&opt);
	if(ret<0)
	{
		printf("could not open video codec :%sn",av_err2str(ret));//無(wú)法打開視頻編解碼器
		exit(1);
	}
	/*視頻輸出*/
	ost->frame=alloc_picture(AV_PIX_FMT_YUV420P,c->width,c->height);
	if(ost->frame==NULL)
	{
		printf("could not allocate video framen");/*無(wú)法分配視頻幀*/
		exit(1);
	}
	printf("ost->frame alloc success fmt=%d w=%d h=%dn",c->pix_fmt,c->width,c->height);
	ost->tmp_frame=NULL;
	if(c->pix_fmt!=AV_PIX_FMT_YUV420P)
	{
		ost->tmp_frame=alloc_picture(AV_PIX_FMT_YUV420P,c->width, c->height);/*視頻幀格式*/
		if(ost->tmp_frame==NULL)
		{
			printf("conld not allocate temporary picturen");/*無(wú)法分配臨時(shí)幀*/
			exit(1);
		}
	}
	/*根據(jù)提供的編解碼器上下文中的值填充參數(shù)結(jié)構(gòu)。*/
	ret=avcodec_parameters_from_context(ost->st->codecpar,c);
	if(ret)
	{
		printf("Could not copy the stream parametersn");/*無(wú)法復(fù)制流參數(shù)*/
		exit(1);
	}

}
static void log_packet(const AVFormatContext * fmt_ctx, const AVPacket * pkt)
{
	AVRational *time_base=&fmt_ctx->streams[pkt->stream_index]->time_base;
    printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%dn",
           									av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
           									av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
           									av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
           									pkt->stream_index);
}

/*寫入數(shù)據(jù)*/
static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
	/*數(shù)據(jù)包中的有效時(shí)間字段(時(shí)間戳/持續(xù)時(shí)間)從一個(gè)時(shí)基轉(zhuǎn)換為另一個(gè)時(shí)基。*/
	av_packet_rescale_ts(pkt,*time_base,st->time_base);
	pkt->stream_index=st->index;
	/*打印信息*/
	log_packet(fmt_ctx, pkt);
	return av_interleaved_write_frame(fmt_ctx, pkt);/*將數(shù)據(jù)包寫入輸出媒體文件,確保正確的交織。*/
}
static int write_video_frame(AVFormatContext * oc, OutputStream * ost,AVFrame *frame)
{
	int ret;
	AVCodecContext *c;
	int got_packet=0;
	AVPacket pkt={0};
	if(frame==(void *)-1)return 1;
	c=ost->enc;
	/*使用默認(rèn)值初始化數(shù)據(jù)包*/
	av_init_packet(&pkt);
	
	/*對(duì)一幀視頻進(jìn)行編碼。*/
	ret=avcodec_encode_video2(c,&pkt,frame,&got_packet);
	if(ret<0)
	{
		printf("Error encoding video frame:%sn",av_err2str(ret));//編碼視頻流錯(cuò)誤
		exit(1);
	}
	printf("--------vidoe pkt.pts=%sn",av_ts2str(pkt.pts));
	printf("------st.num=%d st.den=%d codec.num=%d codec.den=%d-------n",ost->st->time_base.num,
															  ost->st->time_base.den,
															  c->time_base.num,
															  c->time_base.den);
	if(got_packet)
	{
		ret=write_frame(oc,&c->time_base,ost->st,&pkt);/*寫入流數(shù)據(jù)*/
	}
	else
	{
		ret=0;
	}
	if(ret<0)
	{
		printf("Error while writing video frame:%sn",av_err2str(ret));/*寫入流出錯(cuò)*/
		exit(1);
	}
	return (frame||got_packet)?0:1;
}
static AVFrame *get_video_frame(OutputStream *ost,IntputDev* input, int *got_pic)
{
	
	int ret,got_picture;
	AVCodecContext *c=ost->enc;
	AVFrame *ret_frame=NULL;
	/*在各自的時(shí)基中比較兩個(gè)時(shí)間戳。*/
	if(av_compare_ts(ost->next_pts,c->time_base,STREAM_DURATION, (AVRational){1,1})>=0)
	{
		return  (void*)-1;
	}
	/*確保幀數(shù)據(jù)可寫,盡可能避免數(shù)據(jù)復(fù)制。*/
	if(av_frame_make_writable(ost->frame)<0)
	{
		exit(1);
	}
	/*此函數(shù)返回文件中存儲(chǔ)的內(nèi)容,并且不驗(yàn)證是否存在解碼器的有效幀。*/
	if(av_read_frame(input->v_ifmtCtx,input->in_packet)>=0)
	{
		if(input->in_packet->stream_index == input->videoindex)
		{
			/*解碼一幀視頻數(shù)據(jù)。輸入一個(gè)壓縮編碼的結(jié)構(gòu)體AVPacket,輸出一個(gè)解碼后的結(jié)構(gòu)體AVFrame*/
			ret=avcodec_decode_video2(input->pcodecCtx, input->pFrame,&got_picture,input->in_packet);
			*got_pic=got_picture;
			if(ret<0)
			{
				printf("Decode Error.n");
				av_packet_unref(input->in_packet);
				return NULL;
			}
			if(got_picture)
			{
				sws_scale(input->img_convert_ctx, (const unsigned char * const *)input->pFrame->data,input->pFrame->linesize,0,input->pcodecCtx->height,input->pFrameYUV->data,input->pFrameYUV->linesize);
				sws_scale(input->img_convert_ctx, (const unsigned char * const *)input->pFrame->data,input->pFrame->linesize,0,input->pcodecCtx->height,ost->frame->data,ost->frame->linesize);
				pthread_mutex_lock(&fastmutex);//互斥鎖上鎖
				memcpy(rgb_buff,input->pFrameYUV->data[0],size);
				pthread_cond_broadcast(&cond);//廣播喚醒所有線程
				pthread_mutex_unlock(&fastmutex);//互斥鎖解鎖
				ost->frame->pts=ost->next_pts++;
				ret_frame=ost->frame;
			}
		}
		av_packet_unref(input->in_packet);
	}
	return ret_frame;
}
static void close_stream(AVFormatContext * oc, OutputStream * ost)
{
	avcodec_free_context(&ost->enc);
	av_frame_free(&ost->frame);
	av_frame_free(&ost->tmp_frame);
	sws_freeContext(ost->sws_ctx);
	swr_free(&ost->swr_ctx);
}

int main()
{
	/*創(chuàng)建攝像頭采集線程*/
	pthread_t pthid[2];
    pthread_create(&pthid[0],NULL,Video_CollectImage, NULL);
	pthread_detach(pthid[0]);/*設(shè)置分離屬性*/
	sleep(1);
	while(1)
	{
		if(width!=0 && height!=0 && size!=0)break;
		if(video_flag==0)return 0;
	}
	printf("image:%d * %d,%dn",width,height,size);
	unsigned char *rgb_data=malloc(size);
	/*創(chuàng)建mp4視頻編碼線程*/
	pthread_create(&pthid[1],NULL,Video_savemp4, NULL);
	pthread_detach(pthid[1]);/*設(shè)置分離屬性*/
 	/*創(chuàng)建窗口 */
	SDL_Window *window=SDL_CreateWindow("SDL_VIDEO", SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,800,480,SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_RESIZABLE);
    /*創(chuàng)建渲染器*/
	SDL_Renderer *render=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
	/*清空渲染器*/
	SDL_RenderClear(render);
   /*創(chuàng)建紋理*/
	SDL_Texture*sdltext=SDL_CreateTexture(render,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,width,height);
	bool quit=true;
	SDL_Event event;
	SDL_Rect rect;
	int count=0;
	while(quit)
	{
		while(SDL_PollEvent(&event))/*事件監(jiān)測(cè)*/
		{
			if(event.type==SDL_QUIT)/*退出事件*/
			{
				quit=false;
				video_flag=0;
				pthread_cancel(pthid[1]);/*殺死指定線程*/
				pthread_cancel(pthid[0]);/*殺死指定線程*/
				continue;
			}
			else if(event.type == SDL_KEYDOWN)
			{
				 if(event.key.keysym.sym==SDLK_q)//按‘q’保存視頻
				 {
					count++;
					snprintf(file_name,sizeof(file_name),"%d.mp4",count);
					mp4_decode_stat=1;
				 }
			}
		}
		if(!video_flag)
		{
			quit=false;
			continue;
		}
		pthread_mutex_lock(&fastmutex);//互斥鎖上鎖
		pthread_cond_wait(&cond,&fastmutex);
		memcpy(rgb_data,rgb_buff,size);
		pthread_mutex_unlock(&fastmutex);//互斥鎖解鎖
		SDL_UpdateTexture(sdltext,NULL,rgb_data,width);
		//SDL_RenderCopy(render, sdltext, NULL,NULL); // 拷貝紋理到渲染器
		SDL_RenderCopyEx(render, sdltext,NULL,NULL,0,NULL,SDL_FLIP_NONE);
		SDL_RenderPresent(render); // 渲染
	}
	SDL_DestroyTexture(sdltext);/*銷毀紋理*/
    SDL_DestroyRenderer(render);/*銷毀渲染器*/
    SDL_DestroyWindow(window);/*銷毀窗口 */
    SDL_Quit();/*關(guān)閉SDL*/
    pthread_mutex_destroy(&fastmutex);/*銷毀互斥鎖*/
    pthread_cond_destroy(&cond);/*銷毀條件變量*/
	free(rgb_buff);
	free(rgb_data);
	return 0;
}

void *Video_CollectImage(void *arg)
{
	int res=0;
	AVFrame *Input_pFrame=NULL;
	AVFrame *Output_pFrame=NULL;
	printf("pth:%sn",avcodec_configuration());
	/*注冊(cè)設(shè)備*/
	avdevice_register_all();
	/*查找輸入格式*/
	AVInputFormat *ifmt=av_find_input_format("video4linux2");
	if(ifmt==NULL)
	{
		printf("av_find_input_format failedn");
		video_flag=0;
		return 0;
	}
	/*打開輸入流并讀取頭部信息*/
	AVFormatContext *ps=NULL;
	//分配一個(gè)AVFormatContext。
	ps=avformat_alloc_context();	
	res=avformat_open_input(&ps,VIDEO_DEV,ifmt,NULL);
	if(res)
	{
		printf("open input failedn");
		video_flag=0;
		return 0;
	}
	/*查找流信息*/
	res=avformat_find_stream_info(ps,NULL);
	if(res)
	{
		printf("find stream failedn");
		video_flag=0;
		return 0;
	}
	/*打印有關(guān)輸入或輸出格式信息*/
	av_dump_format(ps, 0, "video4linux2", 0);
	/*尋找視頻流*/
	int videostream=-1;
	videostream=av_find_best_stream(ps,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
	printf("videostram=%dn",videostream);
	/*尋找編解碼器*/
	AVCodec *video_avcodec=NULL;/*保存解碼器信息*/
	AVStream *stream = ps->streams[videostream];
	AVCodecContext *context=stream->codec;
	video_avcodec=avcodec_find_decoder(context->codec_id);
	if(video_avcodec==NULL)
	{
		printf("find video decodec failedn");
		video_flag=0;
		return 0;
	}	
	/*初始化音視頻解碼器*/
	res=avcodec_open2(context,video_avcodec,NULL);
	if(res)
	{
		printf("avcodec_open2 failedn");
		video_flag=0;
		return 0;
	}	
	AVPacket *packet=av_malloc(sizeof(AVPacket));/*分配包*/
	AVFrame *frame=av_frame_alloc();/*分配視頻幀*/
	AVFrame *frameyuv=av_frame_alloc();/*申請(qǐng)YUV空間*/
	/*分配空間,進(jìn)行圖像轉(zhuǎn)換*/
	width=context->width;
	height=context->height;
	int fmt=context->pix_fmt;/*流格式*/
	size=av_image_get_buffer_size(AV_PIX_FMT_YUV420P,width,height,16);
	unsigned char *buff=NULL;
	printf("w=%d,h=%d,size=%dn",width,height,size);	
	buff=av_malloc(size);
	rgb_buff=malloc(size);//保存RGB顏色數(shù)據(jù)
	/*存儲(chǔ)一幀圖像數(shù)據(jù)*/
	av_image_fill_arrays(frameyuv->data,frameyuv->linesize,buff,AV_PIX_FMT_YUV420P,width,height, 16);	
	/*轉(zhuǎn)換上下文,使用sws_scale()執(zhí)行縮放/轉(zhuǎn)換操作。*/
	struct SwsContext *swsctx=sws_getContext(width,height, fmt,width,height, AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
	/*視頻輸入流信息*/
	video_input.img_convert_ctx=swsctx;//格式轉(zhuǎn)換上下文
	video_input.in_packet=packet;//數(shù)據(jù)包
	video_input.pcodecCtx=context;
	video_input.pCodec=video_avcodec;/*保存解碼器信息*/
	video_input.v_ifmtCtx=ps;//輸入流并讀取頭部信息
	video_input.videoindex=videostream;/*視頻流*/
	video_input.pFrame=frame;/*視頻幀*/
	video_input.pFrameYUV=frameyuv;/*申請(qǐng)YUV空間*/
	/*讀幀*/
	int go=0;
	int Framecount=0;
	while(video_flag)
	{
		if(!mp4_decode_stat)
		{
			res=av_read_frame(ps,packet);//讀取數(shù)據(jù)
			if(res>=0)
			{
				if(packet->stream_index == AVMEDIA_TYPE_VIDEO)//視頻流
				{
					/*解碼一幀視頻數(shù)據(jù)。輸入一個(gè)壓縮編碼的結(jié)構(gòu)體AVPacket,輸出一個(gè)解碼后的結(jié)構(gòu)體AVFrame*/
					res=avcodec_decode_video2(ps->streams[videostream]->codec,frame,&go,packet);
					if(res<0)
					{
						printf("avcodec_decode_video2 failedn");
						break;
					}
					if(go)
					{
						/*轉(zhuǎn)換像素的函數(shù)*/
						sws_scale(swsctx,(const uint8_t * const*)frame->data,frame->linesize,0,context->height,frameyuv->data,frameyuv->linesize);
						pthread_mutex_lock(&fastmutex);//互斥鎖上鎖
						memcpy(rgb_buff,buff,size);
						pthread_cond_broadcast(&cond);//廣播喚醒所有線程
						pthread_mutex_unlock(&fastmutex);//互斥鎖解鎖
					}
				}
			}
		}
	}
	sws_freeContext(swsctx);/*釋放上下文*/
	av_frame_free(&frameyuv);/*釋放YUV空間*/
	av_packet_unref(packet);/*釋放包*/
	av_frame_free(&frame);/*釋放視頻幀*/
	avformat_close_input(&ps);/*關(guān)閉流*/

	sws_freeContext(video_input.img_convert_ctx);
	avcodec_close(video_input.pcodecCtx);
	av_free(video_input.pFrameYUV);
	av_free(video_input.pFrame);
	avformat_close_input(&video_input.v_ifmtCtx);
	video_flag=0;
	pthread_exit(NULL);	
}
/*MP4格式數(shù)據(jù)保存*/
void *Video_savemp4(void*arg)
{
	while(1)
	{
		if(mp4_decode_stat)
		{
			int res;
			AVFormatContext *oc=NULL;
			AVDictionary *opt=NULL;
			/* 創(chuàng)建的AVFormatContext結(jié)構(gòu)體。*/
			avformat_alloc_output_context2(&oc,NULL,"mp4",NULL);//通過(guò)文件名創(chuàng)建
			if(oc==NULL)
			{
				printf("為輸出格式分配AVFormatContext失敗n");
				avformat_alloc_output_context2(&oc,NULL,"mp4",NULL);//通過(guò)文件名創(chuàng)建
				return 0;
			}
			if(oc==NULL)return (void*)1;
			/*輸出流信息*/
			AVOutputFormat *ofmt=oc->oformat;
			printf("ofmt->video_codec=%dn",ofmt->video_codec);
			int have_video=1;
			int encode_video=0;
			OutputStream video_st={0};
			if(ofmt->video_codec !=AV_CODEC_ID_NONE)
			{
				/*添加一個(gè)輸出流*/
				add_stream(&video_st,oc,&video_input.pCodec,ofmt->video_codec);
				have_video=1;
				encode_video=1;
			}
			printf("w=%d,h=%d,size=%dn",width,height,size);
			/*視頻幀處理*/
			if(have_video)open_video(oc,video_input.pCodec,&video_st,opt);
			printf("打開輸出文件成功rn");
			/*打印有關(guān)輸入或輸出格式信息*/
			av_dump_format(oc, 0,file_name,1);
			if(!(ofmt->flags & AVFMT_NOFILE))
			{
				/*打開輸出文件,成功之后創(chuàng)建的AVFormatContext結(jié)構(gòu)體*/
				res=avio_open(&oc->pb,file_name,AVIO_FLAG_WRITE);
				if(res<0)
				{
					printf("%s open failed :%sn",file_name,av_err2str(res));
					return  (void*)1;
				}
			}
			/*寫入流數(shù)據(jù)頭*/
			res=avformat_write_header(oc,&opt);
			if(res<0)
			{
				printf("open output faile:%sn",av_err2str(res));
				return  (void*)1;
			}
			if(res<0)
			{
				printf("open output faile:%sn",av_err2str(res));
				return  (void*)1;
			}
			int got_pic;
			while(encode_video)
			{
				/*獲取流數(shù)據(jù)*/
				AVFrame *frame=get_video_frame(&video_st,&video_input,&got_pic);
				if(!got_pic || frame==NULL)
				{
					usleep(10000);
					continue;
				}
				encode_video=!write_video_frame(oc,&video_st,frame);
			}
			/*將流數(shù)據(jù)寫入輸出媒體文件并釋放文件私有數(shù)據(jù)。*/
			av_write_trailer(oc);
			/*關(guān)閉AVIOContext*s訪問(wèn)的資源,釋放它,并將指向它的指針設(shè)置為NULL*/
			if(!(ofmt->flags & AVFMT_NOFILE))avio_closep(&oc->pb);
			/*釋放AVFormatContext及其所有流。*/
			avformat_free_context(oc);
			/*關(guān)閉流*/
			if(have_video)close_stream(oc, &video_st);
			mp4_decode_stat=0;
		}
	}
}

2.2Makefile文件

OBJ=get_cameraimage.c
CFLAGS =-I/home/wbyq/src_pack/SDL2-2.0.14/_install/include -I/home/wbyq/src_pack/SDL2-2.0.14/_install/include/SDL2 -L/home/wbyq/src_pack/SDL2-2.0.14/_install/lib
CFLAGS +=-L/home/wbyq/src_pack/SDL2_image-2.0.5/_install/lib -I/home/wbyq/src_pack/SDL2_image-2.0.5/_install/include -I/home/wbyq/src_pack/SDL2_image-2.0.5/_install/include/SDL2
CFLAGS+=-lSDL2 -lpthread -lm -ldl -lSDL2_image -lavcodec -lavfilter -lavutil -lswresample -lavdevice -lavformat -lpostproc -lswscale -lpthread -lstdc++ -lm -lasound

CFLAGS+=-I/home/wbyq/src_pack/ffmpeg-4.2.5/_install/include -L/home/wbyq/src_pack/ffmpeg-4.2.5/_install/lib -I/home/wbyq/src_pack/SDL2-2.0.14/_install/include -I/home/wbyq/src_pack/SDL2-2.0.14/_install/include/SDL2 
app:
	gcc $(OBJ) $(CFLAGS)

3.相關(guān)函數(shù)介紹

3.1 初始化AVFormatContext結(jié)構(gòu)體avformat_alloc_output_context2

int avformat_alloc_output_context2(AVFormatContext **ctx,
??????????????????ff_const59 AVOutputFormat *oformat,
??????????????????const char *format_name, const char *filename);
函數(shù)功能:初始化包含有輸出碼流(AVStream)和解復(fù)用器(AVInputFormat)的AVFormatContext
形參: ctx 函數(shù)調(diào)用成功之后創(chuàng)建的AVFormatContext結(jié)構(gòu)體。
???oformat 指定AVFormatContext中的AVOutputFormat,用于確定輸出格式。如果指定為NULL,可以設(shè)定后兩個(gè)參數(shù)(format_name或者filename)由FFmpeg猜測(cè)輸出格式。
???format_name 指定輸出格式的名稱。根據(jù)格式名稱,F(xiàn)Fmpeg會(huì)推測(cè)輸出格式。輸出格式可以是“flv”,“mkv”等等。
???filename 指定輸出文件的名稱。根據(jù)文件名稱,F(xiàn)Fmpeg會(huì)推測(cè)輸出格式。文件名稱可以是“xx.flv”,“yy.mkv”等等。
返回值: 函數(shù)執(zhí)行成功的話,其返回值大于等于0。

3.2 打開或創(chuàng)建文件avio_open2

int avio_open2(AVIOContext **s, const char *url, int flags,
???????const AVIOInterruptCB *int_cb, AVDictionary **options);
函數(shù)功能:打開或創(chuàng)建文件,函數(shù)調(diào)用成功之后創(chuàng)建的AVIOContext結(jié)構(gòu)體。
形參: s 成功返回AVIOContext結(jié)構(gòu)體。
??? url 輸入輸出協(xié)議的地址(文件也是一種“廣義”的協(xié)議,對(duì)于文件來(lái)說(shuō)就是文件的路徑)。
??? flags打開地址的方式??梢赃x擇只讀,只寫,或者讀寫。取值如下。
??????AVIO_FLAG_READ:只讀。
??????AVIO_FLAG_WRITE:只寫。
??????AVIO_FLAG_READ_WRITE:讀寫。
???后兩個(gè)參數(shù)一般填NULL
返回值: 成功返回>=0

3.3 寫信息頭avformat_write_header

int avformat_write_header(AVFormatContext *s, AVDictionary **options);
函數(shù)功能:將封裝格式的信息寫入文件頭部位置。
形參: s 用于輸出的AVFormatContext。
???options額外的選項(xiàng),一般為NULL
返回值: 函數(shù)正常執(zhí)行后返回值等于0。
還有另外兩個(gè)寫數(shù)據(jù)函數(shù):
??寫視頻數(shù)據(jù):av_write_frame()
??寫視頻文件尾:av_write_trailer()

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 攝像頭
    +關(guān)注

    關(guān)注

    59

    文章

    4793

    瀏覽量

    95279
  • 視頻編碼
    +關(guān)注

    關(guān)注

    2

    文章

    112

    瀏覽量

    21007
  • ffmpeg
    +關(guān)注

    關(guān)注

    0

    文章

    46

    瀏覽量

    7372
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    基于FFMPEG采集攝像頭圖像編碼MP4視頻+時(shí)間水印

    基于FFMPEG采集攝像頭圖像編碼MP4視頻+時(shí)間水
    的頭像 發(fā)表于 09-29 15:46 ?3388次閱讀
    基于<b class='flag-5'>FFMPEG</b><b class='flag-5'>采集</b><b class='flag-5'>攝像頭</b><b class='flag-5'>圖像</b><b class='flag-5'>編碼</b><b class='flag-5'>MP4</b><b class='flag-5'>視頻</b>+時(shí)間水印

    Linux下V4L2框架基于SDL庫(kù)本地USB攝像頭監(jiān)控

    開啟攝像頭采集:VIDIOC_STREAMON (視頻捕獲格式) (7)從采集隊(duì)列中取出圖像數(shù)據(jù)
    的頭像 發(fā)表于 12-17 15:37 ?3753次閱讀
    Linux下V<b class='flag-5'>4</b>L2框架基于<b class='flag-5'>SDL</b>庫(kù)本地USB<b class='flag-5'>攝像頭</b>監(jiān)控

    labview 中咋樣按幀讀取MP4格式視頻文件?

    最近在做視頻按幀讀取的項(xiàng)目,labview中自帶的只有avi格式視頻讀取模塊,現(xiàn)在想把MP4格式視頻
    發(fā)表于 12-21 11:09

    分享一種MP4格式并存儲(chǔ)到TF卡的實(shí)現(xiàn)

    視頻打包為MP4格式并存儲(chǔ)到TF卡的實(shí)現(xiàn)-第7/11季 互聯(lián)網(wǎng)課程品牌《朱老...
    發(fā)表于 12-23 06:21

    請(qǐng)問(wèn)CH32V307可以通過(guò)攝像頭采集數(shù)據(jù)并保存為視頻文件嗎?

    視頻文件,YUV422格式數(shù)據(jù)流可以按照規(guī)則填充文件數(shù)據(jù),成為YUV后綴名視頻文件。但是目前主流的MP4、MKV文件都是帶壓縮的文件。常規(guī)做法是將原始數(shù)據(jù)流傳輸至電腦(可以使用USB、
    發(fā)表于 05-12 09:08

    基于AM57x+Artix-7開發(fā)板——GStreamer視頻開發(fā)案例(3)

    從網(wǎng)絡(luò)攝像頭獲取網(wǎng)絡(luò)流數(shù)據(jù),并調(diào)用IVA-HD將視頻硬件解碼為NV12格式,然后通過(guò)OpenCL調(diào)用DSP端對(duì)NV12格式圖像進(jìn)行Sobel
    發(fā)表于 05-30 17:32

    如何在Android平臺(tái)實(shí)現(xiàn)對(duì)攝像頭數(shù)據(jù)采集并發(fā)送H.264格式視頻數(shù)據(jù)呢

    本帖子主要展示如何在Android平臺(tái)實(shí)現(xiàn)對(duì)攝像頭數(shù)據(jù)采集,再通過(guò)JNI調(diào)用ffmpeg庫(kù)壓制成H.264格式視頻數(shù)據(jù),再通過(guò)簡(jiǎn)單UDP協(xié)議外發(fā)到WIN平臺(tái)的簡(jiǎn)易例程,沒(méi)有實(shí)現(xiàn)RTS
    發(fā)表于 07-01 16:56

    mp4文件偽裝攝像頭畫面

    電子小白,在網(wǎng)上苦苦尋求方案,請(qǐng)各位路過(guò)大俠指點(diǎn): 主管交代,要弄一個(gè)Android設(shè)備。 能夠用 mp4 視頻文件偽裝成攝像頭畫面,然后循環(huán)播放。 不知道能不能實(shí)現(xiàn)呢,請(qǐng)大家提供下思路。謝謝
    發(fā)表于 05-10 18:37

    【KV260視覺入門套件試用體驗(yàn)】2.PS端視頻采集FFMPEG編碼開發(fā)測(cè)試

    令./v4l2_capture –m list –d /dev/video0,可見到輸出信息如下:可見視頻采集攝像頭為uvc驅(qū)動(dòng),支持兩種視頻格式
    發(fā)表于 09-11 00:52

    MP4視頻錄制格式主要有哪些

    MP4視頻錄制格式主要有哪些  視頻錄制格式是指MP4通過(guò)AV接口與電視相連錄制
    發(fā)表于 12-21 15:57 ?2304次閱讀

    視頻怎么壓縮為MP4格式

    MP4格式,那么,怎樣導(dǎo)出視頻MP4格式呢?下面我們一起來(lái)看一下? ? 使用工具:迅捷視頻壓縮
    發(fā)表于 09-05 18:25 ?1839次閱讀

    如何通過(guò)簡(jiǎn)單的步驟將視頻從Final Cut Pro中導(dǎo)出到MP4格式

    如何通過(guò)簡(jiǎn)單的步驟將視頻從Final Cut Pro中導(dǎo)出到MP4格式 ? Final Cut Pro是否支持MP4?是的!Final Cut Pro X支持
    的頭像 發(fā)表于 12-08 15:17 ?8w次閱讀

    在MAC上播放MP4視頻的快速解決方案

    MP4格式是一套用于音頻、視頻信息的壓縮編碼標(biāo)準(zhǔn),由國(guó)際標(biāo)準(zhǔn)化組織(ISO)和國(guó)際電工委員會(huì)(IEC)下屬的“動(dòng)態(tài)圖像專家組”(Moving
    的頭像 發(fā)表于 05-19 17:52 ?5450次閱讀
    在MAC上播放<b class='flag-5'>MP4</b><b class='flag-5'>視頻</b>的快速解決方案

    怎樣快速把MP4文件轉(zhuǎn)成3GP格式

    MP4是當(dāng)今市面上最為常見的視頻格式之一。很多時(shí)候,人們?cè)诰W(wǎng)上找到、下載的視頻都是MP4格式的。不過(guò),M
    的頭像 發(fā)表于 05-19 16:43 ?4289次閱讀
    怎樣快速把<b class='flag-5'>MP4</b>文件轉(zhuǎn)成3GP<b class='flag-5'>格式</b>

    如何快速將QLV格式轉(zhuǎn)換至MP4格式

    QLV視頻格式是騰訊視頻的獨(dú)有格式,并且不是一種常規(guī)的視頻格式,觀看QLV格式視頻文件,必須下
    發(fā)表于 06-03 09:41 ?1531次閱讀
    如何快速將QLV<b class='flag-5'>格式</b>轉(zhuǎn)換至<b class='flag-5'>MP4</b><b class='flag-5'>格式</b>