三、基于FFmpeg和SDL的视频播放器(无声音)

该博客介绍了使用FFmpeg和SDL实现视频播放的方法。包括声明头文件,注册组件初始化,打开文件获取解码器,设置SDL界面,读取数据并播放,还通过SDL线程优化播放流畅度,最后给出了完整代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缅怀雷大神:https://blog.youkuaiyun.com/leixiaohua1020/article/details/38868499

一、 声明头文件

由于FFmpeg和SDL使用纯C来实现的,我们需要声明用C来编译

extern "C"
{
	//封装格式
#include "libavformat/avformat.h"
	//解码
#include "libavcodec/avcodec.h"
	//缩放
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"    //声音文件使用
#include "libavutil/imgutils.h"          //播放图像使用
	//播放
#include "SDL2/SDL.h"
};

二、 注册组件以初始化

av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
	printf("Could not initialize SDL - %s\n", SDL_GetError());
}

三、 打开文件并且获取解码器

filepath:文件路径("G:/迅雷下载/老师好.mp4")

type:数据流类型(音频:AVMEDIA_TYPE_VIDEO)

//打开输入文件
	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0){
		printf("Couldn't open input stream.\n");
		return -1;
	}
	//获取文件信息
	if (avformat_find_stream_info(pFormatCtx, NULL)<0){
		printf("Couldn't find stream information.\n");
		return -1;
	}
	//找到对应的type所在的pFormatCtx->streams的索引位置
	index = -1;
	for (int i = 0; i<pFormatCtx->nb_streams; i++)
		if (pFormatCtx->streams[i]->codec->codec_type == type){
			index = i;
			break;
		}
	if (index == -1){
		printf("Didn't find a video stream.\n");
		return -1;
	}
	//获取解码器
	pCodeCtx = pFormatCtx->streams[index]->codec;
	pCodec = avcodec_find_decoder(pCodeCtx->codec_id);
	if (pCodec == NULL){
		printf("Codec not found.\n");
		return -1;
	}
	//打开解码器
	if (avcodec_open2(pCodeCtx, pCodec, NULL)<0){
		printf("Could not open codec.\n");
		return -1;
	}

四、设置SDL界面

//SDL 2.0 Support for multiple windows
	/*screen_w = pCodeCtx->width;
	screen_h = pCodeCtx->height;*/
	int screen_w = 800;
	int screen_h = 600;
	SDL_Window *screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		screen_w, screen_h, SDL_WINDOW_OPENGL);

	if (!screen) {
		printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
		return -1;
	}
	sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
	sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodeCtx->width, pCodeCtx->height);

五、读取数据并播放

通过SDL线程来优化播放界面不流畅

int sfp_refresh_thread(void *opaque){
	while (1) 
	{
		SDL_Event event;
		event.type = SFM_REFRESH_EVENT;
		SDL_PushEvent(&event);
		SDL_Delay(40);
	}
	return 0;
}
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	//内存分配
	AVFrame	*pFrame = av_frame_alloc();
	AVFrame	*pFrameYUV = av_frame_alloc();
	//缓冲区分配内存
	unsigned char *out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodeCtx->width, pCodeCtx->height, 1));
	//初始化缓冲区
	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,
		AV_PIX_FMT_YUV420P, pCodeCtx->width, pCodeCtx->height, 1);
	img_convert_ctx = sws_getContext(pCodeCtx->width, pCodeCtx->height, pCodeCtx->pix_fmt,
		pCodeCtx->width, pCodeCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

	int ret, got_picture;
	SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
	while (av_read_frame(pFormatCtx, packet) >= 0)
	{
		if (packet->stream_index == index)
		{
			ret = avcodec_decode_video2(pCodeCtx, pFrame, &got_picture, packet);
			if (ret < 0){
				printf("Decode Error.\n");
			}
			if (got_picture)
			{
				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodeCtx->height, pFrameYUV->data, pFrameYUV->linesize);
			}

			while (1)
			{
				SDL_WaitEvent(&event);
				if (event.type == SFM_REFRESH_EVENT)
				{
					//SDL---------------------------
					SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
					SDL_RenderClear(sdlRenderer);
					SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
					SDL_RenderPresent(sdlRenderer);
					//SDL End-----------------------
					break;
				}
			}
		}
	}

六、 完整代码

extern "C"
{
	//封装格式
#include "libavformat/avformat.h"
	//解码
#include "libavcodec/avcodec.h"
	//缩放
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h"
	//播放
#include "SDL2/SDL.h"
};

#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)
int sfp_refresh_thread(void *opaque){
	while (1)
	{
		SDL_Event event;
		event.type = SFM_REFRESH_EVENT;
		SDL_PushEvent(&event);
		SDL_Delay(40);
	}
	return 0;
}

int main(int argc, char* argv[])
{
	AVFormatContext	*pFormatCtx;
	AVCodecContext	*pCodeCtx;
	AVCodec			*pCodec;
	int index;

	struct SwsContext *img_convert_ctx;
	SDL_Renderer* sdlRenderer;
	SDL_Texture* sdlTexture;
	SDL_Thread *video_tid;
	SDL_Event event;

	av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
	}

	char *filepath;
	int type;
	filepath = "G:/迅雷下载/老师好.mp4";
	type = AVMEDIA_TYPE_VIDEO;
	//打开输入文件
	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0){
		printf("Couldn't open input stream.\n");
		return -1;
	}
	//获取文件信息
	if (avformat_find_stream_info(pFormatCtx, NULL)<0){
		printf("Couldn't find stream information.\n");
		return -1;
	}
	//找到对应的type所在的pFormatCtx->streams的索引位置
	index = -1;
	for (int i = 0; i<pFormatCtx->nb_streams; i++)
		if (pFormatCtx->streams[i]->codec->codec_type == type){
			index = i;
			break;
		}
	if (index == -1){
		printf("Didn't find a video stream.\n");
		return -1;
	}
	//获取解码器
	pCodeCtx = pFormatCtx->streams[index]->codec;
	pCodec = avcodec_find_decoder(pCodeCtx->codec_id);
	if (pCodec == NULL){
		printf("Codec not found.\n");
		return -1;
	}
	//打开解码器
	if (avcodec_open2(pCodeCtx, pCodec, NULL)<0){
		printf("Could not open codec.\n");
		return -1;
	}

	//SDL 2.0 Support for multiple windows
	/*screen_w = pCodeCtx->width;
	screen_h = pCodeCtx->height;*/
	int screen_w = 800;
	int screen_h = 600;
	SDL_Window *screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		screen_w, screen_h, SDL_WINDOW_OPENGL);

	if (!screen) {
		printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
		return -1;
	}
	sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
	sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodeCtx->width, pCodeCtx->height);

	AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	//内存分配
	AVFrame	*pFrame = av_frame_alloc();
	AVFrame	*pFrameYUV = av_frame_alloc();
	//缓冲区分配内存
	unsigned char *out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodeCtx->width, pCodeCtx->height, 1));
	//初始化缓冲区
	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,
		AV_PIX_FMT_YUV420P, pCodeCtx->width, pCodeCtx->height, 1);
	img_convert_ctx = sws_getContext(pCodeCtx->width, pCodeCtx->height, pCodeCtx->pix_fmt,
		pCodeCtx->width, pCodeCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

	int ret, got_picture;
	SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
	while (av_read_frame(pFormatCtx, packet) >= 0)
	{
		if (packet->stream_index == index)
		{
			ret = avcodec_decode_video2(pCodeCtx, pFrame, &got_picture, packet);
			if (ret < 0){
				printf("Decode Error.\n");
			}
			if (got_picture)
			{
				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodeCtx->height, pFrameYUV->data, pFrameYUV->linesize);
			}

			while (1)
			{
				SDL_WaitEvent(&event);
				if (event.type == SFM_REFRESH_EVENT)
				{
					//SDL---------------------------
					SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
					SDL_RenderClear(sdlRenderer);
					SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
					SDL_RenderPresent(sdlRenderer);
					//SDL End-----------------------
					break;
				}
			}
		}
	}
	sws_freeContext(img_convert_ctx);
	av_free(out_buffer);
	av_free_packet(packet);
	av_frame_free(&pFrameYUV);
	av_frame_free(&pFrame);
	SDL_Quit();
	avcodec_close(pCodeCtx);
	avformat_close_input(&pFormatCtx);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值