【笔记】An ffmpeg and SDL Tutorial 02《视频播放》


文章《An ffmpeg and SDL Tutorial 02》将的实在是太简略了,我们可以参考雷霄骅的文章《 最简单的视音频播放示例7:SDL2播放RGB/YUV》。


概述

  

在这里使用的版本是SDL2。实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API。在Windows平台下,SDL封装了Direct3D这类的API用于播放视频;封装了DirectSound这类的API用于播放音频。因为SDL的编写目的就是简化视音频播放的开发难度,所以使用SDL播放视频(YUV/RGB)和音频(PCM)数据非常的容易。



SDL简介

SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。用下面这张图可以很明确地说明SDL的用途。

 

SDL实际上并不限于视音频的播放,它将功能分成下列数个子系统(subsystem):

Video(图像):图像控制以及线程(thread)和事件管理(event)。

Audio(声音):声音控制

Joystick(摇杆):游戏摇杆控制

CD-ROM(光盘驱动器):光盘媒体控制

Window Management(视窗管理):与视窗程序设计集成

Event(事件驱动):处理事件驱动

在Windows下,SDL与DirectX的对应关系如下。

SDL

DirectX

SDL_Video、SDL_Image

DirectDraw、Direct3D

SDL_Audio、SDL_Mixer

DirectSound

SDL_Joystick、SDL_Base

DirectInput

SDL_Net

DirectPlay



SDL流程图及主要数据结构

就像FFmpeg一样,SDL2也有固定的执行逻辑,其流程图如下所示:



从上面可以看出,SDL2涉及的数据结构主要有SDL_Window、SDL_Renderer和SDL_Texture。 几个关键的结构体之间的关系可以用下图所示:

C++代码及解释


class CMySDL2
{
public:
	CMySDL2();
	~CMySDL2();

	int					Init(int width, int height);

	int					UnInit();

	void				Tick_Render(AVFrame *pFrame);

	// 消息处理
	void				Tick_Event();

private:
	int					m_nW;
	int					m_nH;
	SDL_Window			*m_pWindow;
	SDL_Renderer		*m_pRenderer;
	SDL_Texture			*m_pTexture;
	SDL_Rect			m_sdlRect;
};


int CMySDL2::Init(int width, int height)
{
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
	{
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}

	//SDL 2.0 Support for multiple windows
	m_nW = width;
	m_nH = height;
	m_pWindow = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		m_nW, m_nH, SDL_WINDOW_OPENGL);
	if (!m_pWindow)
	{
		printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
		return -1;
	}

	m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
	m_pTexture = SDL_CreateTexture(m_pRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, m_nW, m_nH);

	m_sdlRect.x = 0;
	m_sdlRect.y = 0;
	m_sdlRect.w = m_nW;
	m_sdlRect.h = m_nH;

	return 0;
}

int CMySDL2::UnInit()
{
	SDL_Quit();

	return 0;
}

void CMySDL2::Tick_Render(AVFrame *pFrame)
{
	SDL_UpdateTexture(m_pTexture, NULL, pFrame->data[0], m_nW);
	m_sdlRect.x = 0;
	m_sdlRect.y = 0;
	m_sdlRect.w = m_nW;
	m_sdlRect.h = m_nH;

	SDL_RenderClear(m_pRenderer);
	//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  
	SDL_RenderCopy(m_pRenderer, m_pTexture, NULL, &m_sdlRect);
	SDL_RenderPresent(m_pRenderer);
}

void CMySDL2::Tick_Event()
{
	static SDL_Event event;

	SDL_PollEvent(&event);
	switch (event.type) {
	case SDL_QUIT:
		SDL_Quit();
		exit(0);
		break;
	default:
		break;
	}
}

//  函数 函数调用
int main(int argc, char **argv)
{
	my::InitFFmpegLibrary();

	CMyFFmpeg my;
	my.Init("video\\yu_15s.mp4");
	my.Log_StructInfo();

	CMyDecoder dec;
	dec.Init(&my, AV_PIX_FMT_YUV420P);
	AVFrame	*pFrame;

	CMySDL2 mySDL;
	mySDL.Init(my.GetWidth(), my.GetHeight());

	std::ofstream ofile;
	ofile.open("my.yuv", std::ios::binary);

	while (true)
	{
		pFrame = dec.GetFrame();
		if (pFrame)
		{
			mySDL.Tick_Render(pFrame);
		}

		mySDL.Tick_Event();
		Sleep(10);
	}

	mySDL.UnInit();

    return 0;
}


总结

最后总结下编写代码中遇到的两个问题:SDL绘图崩溃、视频花屏。相关代码可以查看GIT的历史记录。

SDL绘图崩溃:avcodec_decode_video2解码出来的数据必须sws_scale后才能使用:因为解出来的可能有黑边,长宽不一定就是视频的长宽。

视频花屏:av_frame_unref(pFrame);只能在程序运行结束后调用。可以查看CMyDecoder的Init和Uninit相关代码。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值