ffmpeg+sdl 实现播放 注释最全 最简

#define __STDC_CONSTANT_MACROS
extern "C"
{
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include<libswscale/swscale.h>
}
#include <SDL.h>
using namespace std;
#define YUV_FORMAT  SDL_PIXELFORMAT_IYUV
//解码后的数据
static AVFrame* frame = NULL;
//解码前的数据
static AVPacket pkt;
SDL_Rect rect;                              // 矩形
SDL_Window* window = NULL;                  // 窗口
SDL_Renderer* renderer = NULL;              // 渲染
SDL_Texture* texture = NULL;                // 纹理
uint32_t pixformat = YUV_FORMAT;            // YUV420P,即是SDL_PIXELFORMAT_IYUV
uint8_t* video_buf = NULL;
size_t video_buff_len = 0;
uint32_t yuv_frame_len = 0;
//视频数据的分辨率
int video_width = 1920;
int video_height = 1080;
//窗口的分辨率
int win_width = 1920;
int win_height = 1080;
void sdl_init()
{
	SDL_Init(SDL_INIT_VIDEO);
	
	//读取到的帧先放到缓冲区里面
	video_buff_len = 0;
	video_buf = NULL;
	
	//该ts文件的流是 YUV420P格式 4个Y分量对应一组uv分量 这里一帧的长度就是yuv_frame_len 为了逻辑清晰才这么写 不然不这么写
	uint32_t y_frame_len = video_width * video_height;
	uint32_t u_frame_len = video_width * video_height / 4;
	uint32_t v_frame_len = video_width * video_height / 4;
	yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;
	//创建窗口
	window = SDL_CreateWindow("My First Player",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		win_width, win_height,
		SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	// 基于窗口创建渲染器
	renderer = SDL_CreateRenderer(window, -1, 0);
	// 基于渲染器创建纹理
	texture = SDL_CreateTexture(renderer,
		pixformat,
		SDL_TEXTUREACCESS_STREAMING,
		video_width,
		video_height);

	video_buf = (uint8_t*)malloc(yuv_frame_len);
}
#undef  main
int main()
{
	//初始化sdl
	sdl_init();
	//解码后的数据要放置的目的地
	FILE* dst_file=NULL;
	//流的索引
	int stream_index=0;
	//流的结构体指针 包括一条流的很多信息
	AVStream* st=NULL;
	//编解码器
	AVCodec* dec = NULL;
	//编解码器上下文
	AVCodecContext* dec_ctx=NULL;
	//申请一个内置的字典? 干啥
	AVDictionary* opts = NULL;
	//复用格式上下文
	AVFormatContext* fmt_cxt = NULL;
	//fmt_cxt = avformat_alloc_context();
	//第一个参数复用格式上下文 第二个参数 视频地址 第三个参数 你希望的视频封装格式 如果设置为空自动检测视频封装格式 最后一个参数可以设置打开码流前的各种参数 比如探测时间 最大延时等
	avformat_open_input(&fmt_cxt, "D:\\vid-hd-f201119101706.ts", NULL, NULL);
	//给这个上下文中的stream这些字段赋值 赋值之后才能继续转换
	avformat_find_stream_info(fmt_cxt, NULL);
	//找到这条流对应的索引 ??为啥要这样干 最大的疑惑这里


	stream_index = av_find_best_stream(fmt_cxt, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);


	//根据索引来拿到对应的流
	st = fmt_cxt->streams[stream_index];
	//给编码器上下文分配内存空间
	avcodec_alloc_context3(dec);
	//根据编码器ID来获取对应的编解码器 得到对应的编解码器过后就可以给编解码器上下文赋值了
	dec = avcodec_find_decoder(st->codecpar->codec_id);
	//给编解码器上下文分配内存空间
	dec_ctx = avcodec_alloc_context3(dec);
	//把流中与编码器相关的信息赋值给编解码器上下文
	avcodec_parameters_to_context(dec_ctx, st->codecpar);
	//最后 打开编解码器 用刚刚的编解码器上下文去打开 因为我们没有设置任何选项 所以填个空的字典就行了
	avcodec_open2(dec_ctx, dec, &opts);
	//至此我们已经根据复用格式和流信息打开了对应的编解码器 接下来就是解码
	//首先打开目的地文件 以可读可写打开
	dst_file = fopen("D:\\11.txt", "wb");
	//给解码后的数据分配内存
	frame = av_frame_alloc();
	//初始化解码前的结构体
	av_init_packet(&pkt);
	pkt.data = NULL;
	pkt.size = 0;
	//写的read frame其实是read packet 从流中读取packet
	//解复用和编解码不一样 不用打开解复用器 直接用解复用上下文就可以解复用
	//只要还能读出一个packet
	while (av_read_frame(fmt_cxt, &pkt) >= 0)
	{
		int ret = 0;
		//解码 packet发送到编解码器解码
		ret = avcodec_send_packet(dec_ctx, &pkt);
		//发送成功
		while (ret >= 0) {
			//收取一帧 已经解码完成的帧
			ret = avcodec_receive_frame(dec_ctx, frame);
			if (ret < 0) {
				if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
					break;
				break;
			}
			//像素转换
			//sws啥意思 slice啥意思 Stride啥意思
			auto img_convert_ctx = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format,
				frame->width, frame->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
			auto tmp_frame = frame;
			//
			sws_scale(img_convert_ctx, (const unsigned char* const*)tmp_frame->data,
				tmp_frame->linesize, 0, tmp_frame->height,
				frame->data, frame->linesize);//avctx->height改成tmp_frame->height
			sws_freeContext(img_convert_ctx);
			//因为这个视频文件的YUV存储格式为 nv21 YUV420sp 就是
			SDL_UpdateYUVTexture(texture, NULL, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]);
			// 显示区域,可以通过修改w和h进行缩放
			rect.x = 0;
			rect.y = 0;
			float w_ratio = win_width * 1.0 / video_width;
			float h_ratio = win_height * 1.0 / video_height;
			// 320x240 怎么保持原视频的宽高比例
			rect.w = video_width * w_ratio;
			rect.h = video_height * h_ratio;
			// 清除当前显示
			SDL_RenderClear(renderer);
			// 将纹理的数据拷贝给渲染器
			SDL_RenderCopy(renderer, texture, NULL, &rect);
			// 显示
			SDL_RenderPresent(renderer);
			//把pkt申请在堆区的资源释放掉 下一次av_read_frame的时候还会继续申请
			av_frame_unref(frame);
			av_packet_unref(&pkt);
		}
		SDL_Delay(40);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要使用FFmpegSDL实现音频播放,您可以按照以下步骤进行操作: 1. 首先,确保您已经安装了FFmpegSDL库。 2. 创建一个C/C++源文件,并包含必要的头文件: ```c #include <stdio.h> #include <SDL2/SDL.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libswresample/swresample.h> ``` 3. 初始化FFmpegSDL库: ```c av_register_all(); avformat_network_init(); SDL_Init(SDL_INIT_AUDIO); ``` 4. 打开音频文件并解码音频流: ```c AVFormatContext *formatCtx = NULL; if (avformat_open_input(&formatCtx, "audio.mp3", NULL, NULL) != 0) { // 处理打开文件失败的情况 return -1; } if (avformat_find_stream_info(formatCtx, NULL) < 0) { // 处理获取流信息失败的情况 return -1; } int audioStreamIndex = -1; for (int i = 0; i < formatCtx->nb_streams; i++) { if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStreamIndex = i; break; } } if (audioStreamIndex == -1) { // 处理没有找到音频流的情况 return -1; } AVCodecParameters *codecPar = formatCtx->streams[audioStreamIndex]->codecpar; AVCodec *codec = avcodec_find_decoder(codecPar->codec_id); if (codec == NULL) { // 处理找不到解码器的情况 return -1; } AVCodecContext *codecCtx = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codecCtx, codecPar) < 0) { // 处理解码器参数设置失败的情况 return -1; } if (avcodec_open2(codecCtx, codec, NULL) < 0) { // 处理打开解码器失败的情况 return -1; } ``` 5. 配置SDL音频参数: ```c SDL_AudioSpec wantedSpec, obtainedSpec; wantedSpec.freq = codecCtx->sample_rate; wantedSpec.format = AUDIO_S16SYS; wantedSpec.channels = codecCtx->channels; wantedSpec.silence = 0; wantedSpec.samples = 1024;wantedSpec.callback = audio_callback; wantedSpec.userdata = codecCtx; SDL_OpenAudio(&wantedSpec, &obtainedSpec); SDL_PauseAudio(0); ``` 6. 实现SDL音频回调函数: ```c void audio_callback(void *userdata, Uint8 *stream, int len) { AVCodecContext *codecCtx = (AVCodecContext *)userdata; AVPacket packet; static uint8_t audio_buffer[(192000 * 3) / 2]; static unsigned int audio_buffer_size = 0; static unsigned int audio_buffer_index = 0; while (len > 0) { if (audio_buffer_index >= audio_buffer_size) { int ret = av_read_frame(formatCtx, &packet); if (ret < 0) { // 处理读取音频帧失败的情况 break; } if (packet.stream_index == audioStreamIndex) { ret = avcodec_send_packet(codecCtx, &packet); if (ret < 0) { // 处理发送音频帧给解码器失败的情况 break; } ret = avcodec_receive_frame(codecCtx, &frame); if (ret < 0) { // 处理接收解码后的音频帧失败的情况 break; } int data_size = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1); memcpy(audio_buffer, frame->data[0], data_size); audio_buffer_size = data_size; audio_buffer_index = 0; } av_packet_unref(&packet); } int remaining = audio_buffer_size - audio_buffer_index; int to_copy = len > remaining ? remaining : len; memcpy(stream, audio_buffer + audio_buffer_index, to_copy); len -= to_copy; stream += to_copy; audio_buffer_index += to_copy; } } ``` 7. 清理资源并关闭SDLFFmpeg库: ```c SDL_CloseAudio(); SDL_Quit(); avformat_close_input(&formatCtx); avcodec_free_context(&codecCtx); return 0; ``` 以上是一个单的示例,演示了如何使用FFmpegSDL实现音频播放。您可以根据自己的需求进行调整和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杀神李

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值