SDL2.0 + FFmpeg实现视频播放器

之前写了一个SDL+FFmpeg的视频播放器,仔细的人可以发现其播放窗口无法拖拽,现在加入SDL的事件控制,来对其做优化

// 基于FFmpeg用SDL实现一个视频播放器(.h264)
//

/*
AVFormatContext:统领全局的基本结构体。主要用于处理封装格式(FLV/MKV/RMVB等)。
AVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。
AVStream,AVCodecContext:视音频流对应的结构体,用于视音频编解码。
AVFrame:存储非压缩的数据(视频对应RGB/YUV像素数据,音频对应PCM采样数据)
AVPacket:存储压缩数据(视频对应H.264等码流数据,音频对应AAC/MP3等码流数据)
*/

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
#include "SDL2/SDL_thread.h"
}

#define SFM_REFRESH_EVENT (SDL_USEREVENT + 100)
#define SFM_BREAK_EVENT (SDL_USEREVENT + 101)

bool thread_exit = 0;
bool thread_pause = 0;

class SDLHandleEx
{
public:
	SDLHandleEx(int w, int h)
	{
		m_rect.x = 0;
		m_rect.y = 0;
		m_rect.w = w;
		m_rect.h = h;
		SdlInit();
	}

	~SDLHandleEx()
	{
		if (m_pTexture)
		{
			SDL_DestroyTexture(m_pTexture);
		}
		if (m_pRender)
		{
			SDL_DestroyRenderer(m_pRender);
		}
		if (m_pWnd)
		{
			SDL_DestroyWindow(m_pWnd);
		}
		SDL_Quit();
	}

	bool CreateSDLWindow(const char* title, Uint32 flag)
	{
		m_pWnd = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_rect.w, m_rect.h, flag);
		if (!m_pWnd)
		{
			printf("CreateWindows error:%s.\n", SDL_GetError());
			return false;
		}

		m_pRender = SDL_CreateRenderer(m_pWnd, -1, 0);
		if (!m_pRender)
		{
			return false;
		}
		m_pTexture = SDL_CreateTexture(m_pRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, m_rect.w, m_rect.h);
		if (!m_pTexture)
		{
			return false;
		}
		return true;
	}

	void UpdateTexture(AVFrame* pFrame)
	{
		if (!pFrame)
		{
			return;
		}

		//SDL_UpdateTexture(m_pTexture, &m_rect, pFrame->data[0], pFrame->linesize[0]);
		SDL_UpdateYUVTexture(m_pTexture, &m_rect, pFrame->data[0], pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1], pFrame->data[2], pFrame->linesize[2]);

		SDL_RenderClear(m_pRender);
		SDL_RenderCopy(m_pRender, m_pTexture, nullptr, &m_rect);
		SDL_RenderPresent(m_pRender);

		SDL_Delay(40);
	}
private:
	bool SdlInit()
	{
		if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
		{
			printf("sdl_init error:%s\n", SDL_GetError());
			return false;
		}
		return true;
	}
private:
	SDL_Renderer* m_pRender = nullptr;
	SDL_Window* m_pWnd = nullptr;
	SDL_Texture* m_pTexture = nullptr;

	SDL_Rect m_rect;
};

int RefreshThread(void *opaque)
{
	thread_pause = false;
	thread_exit = false;

	while (!thread_exit)
	{
		if (!thread_pause)
		{
			SDL_Event event;
			event.type = SFM_REFRESH_EVENT;
			SDL_PushEvent(&event);
		}
		SDL_Delay(40);
	}

	// 退出逻辑
	SDL_Event event_break;
	event_break.type = SFM_BREAK_EVENT;
	SDL_PushEvent(&event_break);

	thread_exit = 0;
	thread_pause = 0;

	return 0;
}

int main(int argc, char* argv[])
{
	AVFormatContext* pFormatCtx = nullptr;
	AVCodecContext* pCodecCtx = nullptr;
	AVCodec* pCodec = nullptr;  // 解码器
	int iVideoIndex = 0, iRet = 0;

	AVFrame* pFrame = nullptr;
	AVFrame* pFrameYUV = nullptr;
	AVPacket* pPacket = nullptr;
	unsigned char* out_buffer = nullptr;

	SwsContext* pSwsCtx = nullptr;
	FILE* fp_yuv = nullptr;
	int got_picture = 0;
	// sdl
	int nScreen_w = 0, nScreen_h = 0;
	SDL_Window* pScreen = nullptr;      // 播放窗口
	SDL_Renderer* pRender = nullptr;    // 渲染器
	SDL_Texture* pSDLTexture = nullptr; // 纹理
	SDL_Event	sdlEvent;
	char filepath[] = "bigbuckbunny_480x272.h265";

	SDLHandleEx* m_pSDlHandle = nullptr;
	// ffmpeg
	av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();
	if (!pFormatCtx)
	{
		printf("avformat_alloc_context error!\n");
		goto exit;
	}

	// 该函数用于打开多媒体数据并且获得一些相关的信息
	if (avformat_open_input(&pFormatCtx, filepath, nullptr, nullptr) < 0)
	{
		printf("avformat_open_input failed!\n");
		goto exit;
	}

	// 该函数可以读取一部分视音频数据并且获得一些相关的信息
	if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
	{
		printf("avformat_find_stream_info error!\n");
		goto exit;
	}

	iVideoIndex = -1;
	for (int i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			iVideoIndex = i;
			break;
		}
	}

	if (iVideoIndex == -1)
	{
		printf("not find a video stream.\n");
		goto exit;
	}

	pCodecCtx = pFormatCtx->streams[iVideoIndex]->codec;

	nScreen_w = pCodecCtx->width;
	nScreen_h = pCodecCtx->height;
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     // 查找解码器
	if (pCodec == nullptr)
	{
		printf("avcodec_find_decode error.\n");
		goto exit;
	}
	if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
	{
		printf("avcodec_open2 failed!\n");
		goto exit;
	}

	printf("--------------- File Information ----------------\n");
	av_dump_format(pFormatCtx, 0, filepath, 0);
	printf("-------------------------------------------------\n");

	// 解码准备
	pFrame = av_frame_alloc();
	pFrameYUV = av_frame_alloc();
	pPacket = (AVPacket*)av_malloc(sizeof(AVPacket));

	// 申请内存  w*h*1.5(Y:w*h U:w*h/4 V:w*h/4)
	out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));

	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);

	pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, nullptr, nullptr, nullptr);


	m_pSDlHandle = new SDLHandleEx(pCodecCtx->width, pCodecCtx->height);

	if (!m_pSDlHandle->CreateSDLWindow("SDL_TEXT", SDL_WINDOW_OPENGL))
	{
		printf("CreateSDLWindow error:%s\n", SDL_GetError());
		goto exit;
	}

	if (!SDL_CreateThread(RefreshThread, "refresh", nullptr))
	{
		printf("SDL_CreateThrad error:%s!\n", SDL_GetError());
		goto exit;
	}

	while (true)
	{
		SDL_WaitEvent(&sdlEvent);
		if (sdlEvent.type == SFM_REFRESH_EVENT)	// 刷新播放
		{
			// 解码
			while (true)	// 拿一帧停一次
			{
				if (av_read_frame(pFormatCtx, pPacket) < 0)
				{
					thread_exit = true;
				}
				
				if (pPacket->stream_index == iVideoIndex)   // 视频packet
				{
					break;
				}
			}
			iRet = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket);
			if (iRet < 0)
			{
				printf("avcodec_decode_video2 error!\n");
				goto exit;
			}

			if (got_picture)
			{
				sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

				m_pSDlHandle->UpdateTexture(pFrame);
			}
			av_free_packet(pPacket);
		}
		else if (sdlEvent.type == SFM_BREAK_EVENT)	// 停止
		{
			break;
		}
		else if (sdlEvent.type == SDL_KEYDOWN)
		{
			if (sdlEvent.key.keysym.sym == SDLK_SPACE)	// 空格键
			{
				thread_pause = !thread_pause;
			}
		}
		else if (sdlEvent.type == SDL_QUIT) // 退出
		{
			thread_exit = 1;
		}
	}

	// 解码器虽然读完了 但是还有一些帧缓存
	while (true)
	{
		iRet = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket);
		if (iRet < 0)
		{
			break;
		}
		if (!got_picture)
		{
			break;
		}

		sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

		m_pSDlHandle->UpdateTexture(pFrame);
	}

exit:
	if (m_pSDlHandle)
	{
		delete m_pSDlHandle;
		m_pSDlHandle = nullptr;
	}
	if (pSwsCtx)
	{
		sws_freeContext(pSwsCtx);
	}
	if (pFrame)
	{
		av_frame_free(&pFrame);
	}
	if (pFrameYUV)
	{
		av_frame_free(&pFrameYUV);
	}

	if (pCodecCtx)
	{
		avcodec_close(pCodecCtx);
	}
	if (pFormatCtx)
	{
		avformat_close_input(&pFormatCtx);
	}

	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值