FFmpeg+SDL 视频播放器

参考链接:《基于 FFmpeg + SDL 的视频播放器的制作》课程的视频_雷霄骅(leixiaohua1020)的专栏-CSDN博客_雷霄骅ffmpeg视频教程

雷神的基于 FFmpeg + SDL 的视频播放器的制作到这里快进入尾声,下面还有一节添加图形界面的课程,不得不说,雷神的录播课讲的非常详细,令人钦佩。

关于雷神的基于 FFmpeg + SDL 的视频播放器的制作的源代码,我发现在他在界面.h265,h.264的文件时,视频的播放速度还算是正常,但是在播放flv,mp4等封装格式的文件时,速度会比较慢,不是正常的速度,这个他在视频中有讲过修改方法,不过在课件的源代码中给出的不是修改过后的,这里给出正确的修改代码(具体修改方式,雷神在http://www.iqiyi.com/w_19rruoxurx.html视频教程中有所提及)

#include "stdafx.h"

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
	//SDL
#include "sdl/SDL.h"
#include "sdl/SDL_thread.h"

};


//Refresh Event
#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)

int thread_exit = 0;  //定义一个线程结束标志

int sfp_refresh_thread(void* opaque)
{
	while (thread_exit == 0) {
		SDL_Event event;
		event.type = SFM_REFRESH_EVENT;
		SDL_PushEvent(&event);
		SDL_Delay(40);
	}
	return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{

	AVFormatContext* pFormatCtx;
	int				i, videoindex;
	AVCodecContext* pCodecCtx;   //保存了视频(音频)编解码相关信息。
	AVCodec* pCodec;
	char filepath[] = "屌丝男士.mov";
	int frame_cnt = 0;
	//注册组件+初始化
	av_register_all(); 
	avformat_network_init();

	//函数用来申请AVFormatContext类型变量并初始化默认参数,分配内存空间
	pFormatCtx = avformat_alloc_context();
	//打开视频流文件
	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {
		printf("Couldn't open input stream.(无法打开输入流)\n");
		return -1;
	}
	//获取视频文件信息
	if (av_find_stream_info(pFormatCtx) < 0)
	{
		printf("Couldn't find stream information.(无法获取流信息)\n");
		return -1;
	}
	//设置一个视频帧索引
	videoindex = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)  //nb_streams:输入视频的AVStream 个数
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)  //streams:输入视频的AVStream []数组
		{
			videoindex = i; //判断文件是视频流还是音频流,把其中的视频流获取出来
			break;
		}
	if (videoindex == -1)
	{
		printf("Didn't find a video stream.(没有找到视频流)\n");
		return -1;
	}
	pCodecCtx = pFormatCtx->streams[videoindex]->codec; //codec:该流对应的AVCodecContext
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //avcodec_find_decoder():查找解码器;  pCodecCtx->codec_id 编解码器ID
	if (pCodec == NULL)
	{
		printf("Codec not found.(没有找到解码器)\n");
		return -1;
	}
	//打开解码器,参数为AVCodecContext和对应的解码器ID
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
	{
		printf("Could not open codec.(无法打开解码器)\n");
		return -1;
	}

	AVFrame* pFrame, * pFrameYUV; //存储一帧解码后像素(采样)数据
	pFrame = avcodec_alloc_frame(); //为解码帧分配内存
	pFrameYUV = avcodec_alloc_frame();
	//av_malloc()内存分配,uint8_t = unsigned char无符号字符型
	//avpicture_get_size()计算这个格式的图片,需要多少字节来存储

	uint8_t* out_buffer = (uint8_t*)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));

	/*
	pFrameYUV和out_buffer都是已经申请到的一段内存, 但是pFrameYUV只是申请了一段结构体内存, 
	结构体里面的值是空的, 我们需要使用avpicture_fill()函数来使得pFrameYUV和out_buffer关联起来,
	pFrameYUV里面使用的是out_buffer所指向的内存空间.
	*/
	avpicture_fill((AVPicture*)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

	//------------SDL----------------
	//初始化
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}

	int screen_w = 0, screen_h = 0;
	SDL_Window* screen;
	//SDL 2.0 Support for multiple windows
	screen_w = pCodecCtx->width;
	screen_h = pCodecCtx->height;
	//创建一个窗口
	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;
	}

	//创建一个渲染器
	SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
	//IYUV: Y + U + V  (3 planes)
	//YV12: Y + V + U  (3 planes)
	SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);

	SDL_Rect sdlRect; //创建一个矩形结构体,分别给左上角的坐标以及矩形框的宽和高
	sdlRect.x = 0;
	sdlRect.y = 0;
	sdlRect.w = screen_w;
	sdlRect.h = screen_h;

	int ret, got_picture;

	//分配一帧压缩编码数据的内存
	AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
	//Output Info-----------------------------
	printf("File Information(文件信息)---------------------\n");
	//av_dump_format()打印关于输入或输出格式的详细信息
	av_dump_format(pFormatCtx, 0, filepath, 0);
	printf("-------------------------------------------------\n");

	//详解:https://blog.csdn.net/MACMACip/article/details/105450185
	struct SwsContext* img_convert_ctx; 
	img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
	//--------------
	//创建一个线程
	SDL_Thread* video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
	//
	//Event Loop
	SDL_Event event;
	for (;;) {
		//Wait
		SDL_WaitEvent(&event);
		if (event.type == SFM_REFRESH_EVENT) {
			//------------------------------
			//在这里判断是不是视频帧,如果是就直接跳出循环
			while (1) {
				if (av_read_frame(pFormatCtx, packet) < 0) {
					thread_exit = 1;
				}
				if (packet->stream_index == videoindex) {
					break;
				}
			}
			//解码一帧压缩数据
			ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
			if (ret < 0) {
				printf("Decode Error.(解码错误)\n");
				return -1;
			}
			if (got_picture) {
				sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
				//printf("Decoded frame index: %d\n", frame_cnt);
				//frame_cnt++;
				//SDL---------------------------
				SDL_UpdateTexture(sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0]);
				SDL_RenderClear(sdlRenderer);
				SDL_RenderCopy(sdlRenderer, sdlTexture, &sdlRect, &sdlRect);
				SDL_RenderPresent(sdlRenderer);
				//SDL End-----------------------
				//av_free_packet(packet);
			}
			av_free_packet(packet); //释放
		}

	}

	sws_freeContext(img_convert_ctx); 

	SDL_Quit(); //退出线程
	//--------------
	av_free(out_buffer);
	av_free(pFrameYUV);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值