ffmpeg + SDL2 实现播放器(一)

使用的ffmpeg版本为5.0.1,SDL的版本为2.022。c++环境为vs2017。

先上最简易的整体代码,初步实现了SDL和ffmpeg的结合。

#include<iostream>
#include<string.h>
#include<SDL.h>
extern "C"
{
#include "libavformat/avformat.h" //头文件不仅要在项目中鼠标点击配置,在代码中也要引入
#include "include/libavformat/avformat.h"
#include "include/libswscale/swscale.h"
#include "include/libavdevice/avdevice.h"
#include"libavcodec/avcodec.h"
}
using namespace std;

int main(int argc, char *argv[])
{
	int ret = 1;
	const char *file = "ds.mov";

	// 初始化ffmpeg组件
	AVFormatContext *pFormatCtx = nullptr;   //视频文件上下文
	int videostream;   //视频流标识
	AVCodecParameters *pCodeParameters = nullptr; //解码器相关参数
	const AVCodec *pCodec = nullptr;  //解码器
	AVCodecContext *pCodecCtx = nullptr;  // 解码器上下文
	AVFrame *pFrame = nullptr; //解码后的帧
	AVPacket packet; // 解码前的帧

	//初始化SDL组件
	SDL_Rect rect;                              //渲染显示面积
	SDL_Window *window = NULL;                  // 窗口
	SDL_Renderer *renderer = NULL;              // 渲染
	SDL_Texture *texture = NULL;                // 纹理
	Uint32 pixformat;

	//视频分辨率
	int w_width = 640;  
	int w_height = 480;

	//SDL初始化 
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		cout << "can not initialize SDL" << endl;
		return ret;
	}

	//FFMPEG 视频文件读取
	if (avformat_open_input(&pFormatCtx, file, nullptr, nullptr) != 0) {
		cout << "can not open the video file" << endl;
		goto __FAIL;
	}

	//FFMPEG 寻找视频流
	videostream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1,nullptr,0);
	if (videostream == -1) {
		cout << "can not open a video stream" << endl;
		goto __FAIL;
	}

	//FFMPEG 寻找合适的解码器
	pCodeParameters = pFormatCtx->streams[videostream]->codecpar;
	pCodec = avcodec_find_decoder(pCodeParameters->codec_id);
	if (pCodec == nullptr) {
		cout << "can not find a codec" << endl;
		goto __FAIL;
	}

	//FFMPEG 解码器信息配置
	pCodecCtx = avcodec_alloc_context3(pCodec);
	if (avcodec_parameters_to_context(pCodecCtx, pCodeParameters) != 0) {
		cout << "can not copy codec context" << endl;
		goto __FAIL;
	}

	//FFMPEG 解码器启动
	if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
		cout << " can not open the decoder" << endl;
		goto __FAIL;
	}

	//FFMPEG 初始化解码的帧
	pFrame = av_frame_alloc();

	//SDL 获得显示的视频画面的长度与宽度
	w_width = pCodecCtx->width;
	w_height = pCodecCtx->height;

	//SDL 窗口初始化
	window = SDL_CreateWindow("MEDIA PLAYER",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		w_width, w_height,
		SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	if (!window) {
		cout << "can not create window" << endl;
		goto __FAIL;
	}

	//SDL 渲染器初始化
	renderer = SDL_CreateRenderer(window, -1, 0);
	if (!renderer) {
		cout << "can not create renderer!" << endl;
		goto __FAIL;
	}

	//SDL 视频格式与纹理初始化
	pixformat = SDL_PIXELFORMAT_IYUV;
	texture = SDL_CreateTexture(renderer,
		pixformat,
		SDL_TEXTUREACCESS_STREAMING,
		w_width,
		w_height);

	//主循环
	while (av_read_frame(pFormatCtx,&packet)>= 0) {//FFMPEG 如果已经读到了一个帧
		if (packet.stream_index == videostream) {//并且该帧是视频帧
			avcodec_send_packet(pCodecCtx,&packet);
			while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
				//SDL 刷新纹理
				SDL_UpdateYUVTexture(texture, NULL,
					pFrame->data[0], pFrame->linesize[0],
					pFrame->data[1], pFrame->linesize[1],
					pFrame->data[2], pFrame->linesize[2]);
				rect.x = 0;//SDL设置渲染目标的显示区域
				rect.y = 0;
				rect.w = pCodecCtx->width;
				rect.h = pCodecCtx->height;

				SDL_RenderClear(renderer);//SDL 清空渲染器内容
				SDL_RenderCopy(renderer, texture, NULL, &rect);//SDL 将纹理复制到渲染器
				SDL_RenderPresent(renderer);//SDL 渲染
			}
		}
		av_packet_unref(&packet);

		SDL_Event event;// SDL事件
		SDL_PollEvent(&event);// 轮询事件
		switch (event.type)
		{
		case SDL_QUIT: //如果窗口被关闭
			goto __QUIT;
		default:
			break;
		}
	}
	

__QUIT:
	ret = 0;
__FAIL:
	if (pFrame) {
		av_frame_free(&pFrame);
		pFrame = nullptr;
	}
	if (pCodecCtx) {
		avcodec_close(pCodecCtx);
		pCodecCtx = nullptr;
		pCodec = nullptr;
	}
	if (pCodeParameters) {
		avcodec_parameters_free(&pCodeParameters);
		pCodeParameters = nullptr;
	}
	/*if (pFormatCtx) {
		avformat_close_input(&pFormatCtx);
	}*/

	if (pFormatCtx) {
		avformat_free_context(pFormatCtx);
		pFormatCtx = nullptr;
	}
	
	if (texture) {
		SDL_DestroyTexture(texture);
		texture = nullptr;
	}
	if (renderer) {
		SDL_DestroyRenderer(renderer);
		renderer = nullptr;
	}
	if (window) {
		SDL_DestroyWindow(window);
		window = nullptr;
	}

	SDL_Quit();
	cout << "succeed!" << endl;
	return ret;
}

以上代码在关键函数的注释中都已经注明了其属于哪个模块,有助于拆解学习。

以上代码的大部分内容在之前的文章都有所介绍,这里主要讲解新出现的知识。

av_read_frame(pFormatCtx,&packet)

在对视频的帧进行解码时,首先我们需要将帧从视频文件中提取出来,注意当前的帧是未解压缩的,属于packet。复习一下,packet是未解码的帧,frame是解码完成了的帧。

av_read_frame()实现的正是此功能,该函数确切的说是从视频文件中读取一个packet出来。并且该函数可以保证读取的帧是完整的。

而在之前的文章中提到,一个packet只含有一个视频帧,但是可以包含多个音频帧。

对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数帧数。
如果每个帧的大小可变(MPEG),则包含一个帧。

通过将此函数作为主循环的工作条件,可以保证在从视频文件中读到多媒体帧后就工作,读不到多媒体帧就不工作。

avcodec_send_packet(pCodecCtx,&packet)

该函数正是ffmpeg对视频进行解码操作的函数,该函数的工作内容和名字一样,将packet数据传入到codec进行解码,得到解码后的frame数据。

avcodec_receive_frame(pCodecCtx, pFrame)

该函数是与解码操作配合的函数,在codec解码器将packet解码为frame后使用该函数将frame读取出来,frame变量就是当前的解码的帧。

这里注意一下,avcodec_receive_frame(pCodecCtx, pFrame)与avcodec_send_packet(pCodecCtx,&packet)这两个函数是一一对应的么?其实应该不是,因为上文提过,一个packet可能解出多个frame出来(在音频帧的情况下),因此send函数应该是对应多个receive函数的。

当我们不知道一个函数会执行多少次时,我们可以将此函数放入while()循环的判断语句中,当函数的返回值发生变化,说明该函数不需要再被执行,循环内的相关处理语句也不会再执行。

SDL与ffmpeg的结合点

通过之前文章可以知道,SDL是通过将视频帧“刷”到纹理上,之后实现渲染播放的,我们将ffmpeg解码后的frame刷到纹理上,就实现了两者的结合。

SDL_UpdateYUVTexture(texture, NULL,
					pFrame->data[0], pFrame->linesize[0],
					pFrame->data[1], pFrame->linesize[1],
					pFrame->data[2], pFrame->linesize[2]);

刷新纹理的函数中,分别对y、u、v三个通道的数据进行了加载,等于是刷进了纹理,之后的操作就和 SDL播放视频没有什么区别了。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ffmpeg sdl是一种基于ffmpegsdl开发的视频播放器ffmpeg是一个开源的跨平台音视频解决方案,支持包括视频采集、编码、解码、转码、播放等功能,被广泛应用于视频处理和视频播放领域。而sdl是一种跨平台开放源代码的多媒体库,提供了音频、视频、事件等处理功能,它是一个适合于游戏和媒体应用的库。 ffmpeg sdl的视频播放器利用ffmpeg库解码文件,将视频数据解码成RGB格式的图片数据,然后使用sdl库将图片数据渲染到屏幕上。通过这种方式,可以实现基于CPU的视频播放。 ffmpeg sdl的视频播放器具有以下特性: 1. 支持多种视频格式:ffmpeg支持的视频格式非常丰富,几乎可以处理所有常见的视频格式,例如MP4、AVI、FLV等。因此,ffmpeg sdl的视频播放器可以播放多种格式的视频。 2. 良好的兼容性:ffmpeg sdl的视频播放器可以运行在多个平台上,例如Windows、Linux、Android等平台。 3. 可扩展性强:ffmpegsdl都是开源的项目,可以方便地进行开发和扩展。同时,ffmpeg sdl的视频播放器还支持自定义音频和视频渲染。 4. 轻量级:ffmpeg sdl的视频播放器采用的是CPU进行解码,因此不需要额外的硬件加速。同时,ffmpegsdl都是轻量级的库,不会占用过多的系统资源。 总之,ffmpeg sdl的视频播放器是一种非常方便的视频播放解决方案,具有兼容性强、支持视频格式多、可扩展性强等优点。它可以为视频处理和视频播放提供极佳的支持,并且可以适用于多种平台和应用场景。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值