ffmpeg调用directshow camera 并sdl渲染

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ffmpeg调用directshow camera 并sdl渲染相关的知识,希望对你有一定的参考价值。

基础

ffmpeg使用设备先注册

avdevice_register_all();

使用常用得CCameraDS 取获取名称,这里使用第一个摄像头。这里我们使用SDL来渲染得时候,启动一个定时器来定时给一个事件让SDL来刷新

SDLSDL_Init(SDL_INIT_VIDEO |  SDL_INIT_TIMER);

所以使用video和timer来完成这种渲染。

渲染流程

这里使用bgr24来渲染,使用sdl 创建表面,指定opengl来做
1 创建窗口
2 创建刷新线程
3 线程中发送更新事件
4 main主循环捕获事件,获取一帧rgb24,刷新界面渲染

这里面可以修改变成这样
刷新线程渲染获取一帧,推给主线程,主线程负责编码发送,同时渲染,这里是示例,没有这么做。

int main()

	avdevice_register_all();
	int w = 1280;
	int h = 720;
	g_fps = 10;
	std::wstring cname;
	CCameraDS::CameraName(0, cname);
	std::string name = UnicodeToUTF8(cname);
	TFFCap cap;
		
	int ret = cap.OpenCameraRGB(name.c_str(), 1280, 720);
	if (ret != 0)
		return -1;

	int					screen_w, screen_h;
	SDL_Window			*screen;
	SDL_Renderer		*sdlRenderer;
	SDL_Texture			*sdlTexture;
	SDL_Rect			sdlRect;
	SDL_Thread			*video_tid;
	SDL_Event			event;
	
	SDL_Init(SDL_INIT_VIDEO |  SDL_INIT_TIMER);
	screen_w = w; //w;
	screen_h = h;
	//screen_h = screen_w * 9 / 16;
	screen = SDL_CreateWindow("FF", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		screen_w, screen_h, SDL_WINDOW_OPENGL);

	if (!screen)
	
		std::cout << "SDL: could not create window - exiting: " << SDL_GetError() << std::endl;
		return -1;
	
	sdlRenderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
	sdlTexture = SDL_CreateTexture(sdlRenderer,
		SDL_PIXELFORMAT_BGR24 , //SDL_PIXELFORMAT_IYUV, //SDL_PIXELFORMAT_BGR24
SDL_TEXTUREACCESS_STREAMING,/*SDL_TEXTUREACCESS_STREAMING,*/
		
		screen_w, screen_h);
	sdlRect.x = 0;
	sdlRect.y = 0;
	sdlRect.w = screen_w;
	sdlRect.h = screen_h;
	video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
	int got_picture;
	for (;;)
	
		SDL_WaitEvent(&event);
		if (event.type == SFM_REFRESH_EVENT)
		
			uint8_t *frame = cap.QueryFrame();

			if (frame != NULL)
			
				int y = screen_h * 2 / 3;
				SDL_UpdateTexture(sdlTexture, NULL/*&sdlRect*/, frame, w * 3);
				//SDL_UpdateTexture(sdlTexture, &sdlRect, out_buffer + W * H * 3, -W * 3);

				SDL_RenderClear(sdlRenderer);
				SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
				SDL_RenderPresent(sdlRenderer);
			
		
		else if (event.type == SDL_KEYDOWN)
		
			if (event.key.keysym.sym == SDLK_SPACE)
				thread_pause = !thread_pause;
		
		else if (event.type == SDL_QUIT)
		
			thread_exit = 1;
		
		else if (event.type == SFM_BREAK_EVENT)
		
			break;
		
	

SDL使用得事件如下:

int thread_exit = 0;
int thread_pause = 0;

int g_fps = 30;
int sfp_refresh_thread(void *opaque) 

	thread_exit = 0;
	thread_pause = 0;

	while (!thread_exit) 
	
		if (!thread_pause) 
		
			SDL_Event event;
			event.type = SFM_REFRESH_EVENT;
			SDL_PushEvent(&event);
		
		SDL_Delay(1000/g_fps);
	
	thread_exit = 0;
	thread_pause = 0;
	//Break
	SDL_Event event;
	event.type = SFM_BREAK_EVENT;
	SDL_PushEvent(&event);
	return 0;

线程里面每隔1000/g_fps得时间就产生一个渲染事件,取得一帧数据后渲染,当然这里也可以编码发送等等都可以做。事件发生等等也可以使用标准c++得事件等待互斥,原理都是一摸一样得。

最后给出实现得FFcap

TFFCap声明

class TFFCap

public:
	int m_fps = 10;

	AVFrame * m_pFrame = NULL;
	AVFrame * m_pFrameDst = NULL;

	string m_name;

	uint8_t * out_buffer = NULL;

	int m_videoIndex = -1;
	struct SwsContext * m_img_convert_ctx = NULL;
	//int _pixelW;
	//int _pixelH;


private:
	AVFormatContext * m_pFormatCtx = NULL;
	AVCodecContext  * m_pCodecCtx = NULL;
	AVCodec         * m_pCodec = NULL;
	AVPacket        * m_pkt = NULL;
public:
	void Init()
	
		avdevice_register_all();
	
	int OpenCameraRGB(const char * utf8videoname, 
		int nWidth, int nHeight);

	uint8_t *QueryFrame();

	int m_w = 0;
	int m_h = 0;
//	int QueryFrame(uint8_t *buf, int *len);

	void UnInit();
	TFFCap();
	~TFFCap();
;

TFFCap实现

#include "FFCap.h"
#include <stdio.h>
#include <sstream>
using namespace std;



TFFCap::TFFCap()




TFFCap::~TFFCap()




static AVDictionary *GetOptions(int videosize, int &fps)

	AVDictionary *options = NULL;
	switch (videosize)
	
	case 1:
	
		fps = 10;
		av_dict_set(&options, "video_size", "1280x720", 0);
		//const char * fname = av_get_pix_fmt_name((AVPixelFormat)13);
		//av_dict_set(&options, "pixel_format", fname, 0);
		av_dict_set_int(&options, "framerate", 10, 0);
		av_dict_set_int(&options, "rtbufsize", 2764800 / 10, 0);
		av_dict_set(&options, "start_time_realtime", 0, 0);
		//av_dict_set(&options, "mjpeg", 0, 0);
	
	break;
	case 2:
		fps = 20;
		av_dict_set_int(&options, "framerate", 30, 0);
		av_dict_set(&options, "video_size", "640x480", 0);
		av_dict_set_int(&options, "rtbufsize", 640 * 480 * 3 / 15, 0);
		break;
	case 3:
		fps = 20;
		av_dict_set_int(&options, "framerate", 30, 0);
		av_dict_set(&options, "video_size", "320x240", 0);
		av_dict_set_int(&options, "rtbufsize", 76800 / 10, 0);
		break;
	case 4:
		fps = 20;
		av_dict_set_int(&options, "framerate", 30, 0);
		av_dict_set(&options, "video_size", "176x144", 0);
		//av_dict_set_int(&options, "rtbufsize", 76800 / 10, 0);
		break;
	
	av_dict_set(&options, "start_time_realtime", 0, 0);
	return options;




int TFFCap::OpenCameraRGB(const char * utf8videoname,
	int w, int h)

	av_log_set_level(AV_LOG_FATAL);

	AVInputFormat *ifmt = nullptr;
	AVDictionary *options = NULL;


	string oldname = m_name;
	m_name = "video=";
	m_name += utf8videoname;

	if (oldname.compare(m_name) != 0)
	
		avcodec_close(m_pCodecCtx);
		avcodec_free_context(&m_pCodecCtx);
		m_pCodecCtx = NULL;
		avformat_close_input(&m_pFormatCtx);
		m_pFormatCtx = NULL;
	
	else 
	
		if (m_w == w && m_h == h)
			return 0;
		avcodec_free_context(&m_pCodecCtx);
		m_pCodecCtx = NULL;
		avformat_close_input(&m_pFormatCtx);
		m_pFormatCtx = NULL;
	

	if (m_pFormatCtx == NULL)
	
		m_pFormatCtx = avformat_alloc_context();
		ifmt = av_find_input_format("dshow");
	
	m_pFormatCtx->flags |= AVFMT_FLAG_NOBUFFER;
	av_dict_set(&options, "start_time_realtime", 0, 0);
	switch (w)
	
	case 1920:
		break;
	case 1280:
		m_fps = 10;
		av_dict_set(&options, "video_size", "1280x720", 0);
		//av_dict_set_int(&options, "framerate", 10, 0);
		av_dict_set_int(&options, "rtbufsize", 2764800 / 10, 0);
		break;
	case 640:
		m_fps = 20;
		av_dict_set_int(&options, "framerate", 30, 0);
		av_dict_set(&options, "video_size", "640x480", 0);
		av_dict_set_int(&options, "rtbufsize", 640 * 480 * 3 / 15, 0);
		break;
	case 320:
		m_fps = 20;
		av_dict_set_int(&options, "framerate", 30, 0);
		av_dict_set(&options, "video_size", "320x240", 0);
		av_dict_set_int(&options, "rtbufsize", 76800 / 10, 0);
		break;
	case 176:
		m_fps = 20;
		av_dict_set_int(&options, "framerate", 30, 0);
		av_dict_set(&options, "video_size", "176x144", 0);
		av_dict_set_int(&options, "rtbufsize", 76800 / 10, 0);
		break;
	
	if (avformat_open_input(&m_pFormatCtx, m_name.c_str(), ifmt, &options) != 0)
	
		return -1;
	
	if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0)
	
		//std::cout << "avformat find stream info failed." << std::endl;
		return -1;
	


	for (uint32_t i = 0; i < m_pFormatCtx->nb_streams; i++)
	
		if (m_pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		
			//AVPixelFormat avf = (AVPixelFormat)m_pFormatCtx->streams[i]->codecpar->format;
			//const char* strfmt = av_get_pix_fmt_name(avf);
			//w = m_pFormatCtx->streams[i]->codecpar->width;
			//h = m_pFormatCtx->streams[i]->codecpar->height;
			m_videoIndex = i;
			break;
		
	
	if (-1 == m_videoIndex)
	
		return -1;
	
	if(m_pCodecCtx == NULL)
		m_pCodecCtx = avcodec_alloc_context3(NULL);
	if (m_pCodecCtx == NULL)
	
		return -1;
	
	avcodec_parameters_to_context(m_pCodecCtx, m_pFormatCtx->streams[m_videoIndex]->codecpar);
	m_pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
	if (m_pCodec == NULL)
	
		//std::cout << "AVCodec not found." << std::endl;
		return -1;
	
	if (avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0)
	
		//std::cout << "codec open failed." << std::endl;
		return -1;
	
	if(m_pFrame == NULL)
		m_pFrame = av_frame_alloc();
	if(m_pFrameDst == NULL)
		m_pFrameDst = av_frame_alloc();
	if(m_pkt == NULL)
		m_pkt = av_packet_alloc();
	AVPixelFormat avpf = AV_PIX_FMT_BGR24; // AV_PIX_FMT_YUV420P; //AV_PIX_FMT_BGR24
	if (m_w != w || m_h != h)
	
		if (out_buffer != NULL)
			delete []out_buffer;

	
	if (out_buffer == NULL)
	
		out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(avpf,
			w,
			h,
			1));

		av_image_fill_arrays(m_pFrameDst->data, m_pFrameDst->linesize, out_buffer,
			avpf, w, h, 1);
	
	m_img_convert_ctx = 
		sws_getCachedContext(m_img_convert_ctx,
		w, h, m_pCodecCtx->pix_fmt,
		w, h, avpf, SWS_POINT, NULL, NULL, NULL);
	
	m_w = w;
	m_h = h;
	return 0;




uint8_t *TFFCap::QueryFrame(/*int &len*/)

	if (m_pFormatCtx == NULL)
		return NULL;
	AVPacket packet;
	av_init_packet(&packet);

	int got_picture = 0;
	if (av_read_frame(m_pFormatCtx, &packet) >= 0)
	
		int ret = 0;
		if (packet.stream_index == m_videoIndex)
		
			if (avcodec_send_packet(m_pCodecCtx, &packet) == 0)
				if (avcodec_receive_frame(m_pCodecCtx, m_pFrame) == 0)
				
					got_picture = 1;
					sws_scale(m_img_convert_ctx, 
						(const uint8_t* const*)m_pFrame->data, 
						m_pFrame->linesize, 0, 
						m_pCodecCtx->height, 
						m_pFrameDst->data, m_pFrameDst->linesize);
				

			av_packet_unref(&packet);
		
		
	


	//返回上一帧
	if (got_picture)
		return out_buffer;
	return NULL;

void TFFCap::UnInit()

	av_frame_free(&m_pFrameDst);
	av_frame_free(&m_pFrame);

	if(m_img_convert_ctx!= nullptr)
		sws_freeContext(m_img_convert_ctx);

	avcodec_close(m_pCodecCtx);
	avcodec_free_context(&m_pCodecCtx);
	avformat_close_input(&m_pFormatCtx);

效果


代码里面还有一些是可以修改得,仅仅作为示例,代表可行

以上是关于ffmpeg调用directshow camera 并sdl渲染的主要内容,如果未能解决你的问题,请参考以下文章

解构ffmpeg

替代在Directshow中使用ISpecifyPropertyPages的替代编程方式

directshow-faac编码

ffmpeg/camera实现最近很火的视频壁纸,相机壁纸

FFmpeg 在Windows的命令

基于FFmpeg实现屏幕录制