FFmpeg + SDL2 实现简易播放器:视频解码到 YUV 渲染流程
核心流程概述
- 初始化:加载视频文件,准备解码环境
- 解码:提取视频帧并解码为 YUV 数据
- 渲染:通过 SDL2 显示 YUV 帧
- 控制:实现播放状态管理
详细步骤与代码实现
1. 初始化环境
#include <SDL.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
int main() {
// 初始化 SDL2
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Player", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, 1280, 720);
// 初始化 FFmpeg
av_register_all();
AVFormatContext* fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
avformat_find_stream_info(fmt_ctx, NULL);
// 查找视频流
int video_stream_index = -1;
for (int i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
break;
}
}
}
2. 准备解码器
// 获取解码器
AVCodecParameters* codec_params = fmt_ctx->streams[video_stream_index]->codecpar;
AVCodec* codec = avcodec_find_decoder(codec_params->codec_id);
AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, codec_params);
avcodec_open2(codec_ctx, codec, NULL);
// 准备帧结构
AVFrame* frame = av_frame_alloc();
AVPacket* packet = av_packet_alloc();
3. 解码循环
struct SwsContext* sws_ctx = sws_getContext(
codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL
);
while (av_read_frame(fmt_ctx, packet) >= 0) {
if (packet->stream_index == video_stream_index) {
avcodec_send_packet(codec_ctx, packet);
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
// 转换为 YUV420P
AVFrame* yuv_frame = av_frame_alloc();
av_image_alloc(yuv_frame->data, yuv_frame->linesize,
codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P, 1);
sws_scale(sws_ctx, frame->data, frame->linesize, 0,
frame->height, yuv_frame->data, yuv_frame->linesize);
// 渲染到 SDL2
SDL_UpdateYUVTexture(texture, NULL,
yuv_frame->data[0], yuv_frame->linesize[0], // Y 分量
yuv_frame->data[1], yuv_frame->linesize[1], // U 分量
yuv_frame->data[2], yuv_frame->linesize[2] // V 分量
);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
av_frame_free(&yuv_frame);
SDL_Delay(40); // 控制帧率
}
}
av_packet_unref(packet);
}
4. 资源释放
av_frame_free(&frame);
av_packet_free(&packet);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx);
sws_freeContext(sws_ctx);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
关键技术说明
-
YUV 格式:
- 使用
AV_PIX_FMT_YUV420P格式存储帧数据 - YUV 分量分离:Y(亮度)、U/V(色度)
- 内存布局:
data[0]=Y,data[1]=U,data[2]=V
- 使用
-
帧率控制:
- 通过
SDL_Delay()实现简易帧率控制 - 精确控制需计算 PTS(Presentation Time Stamp)
- 通过
-
扩展建议:
- 添加音频解码与同步
- 实现播放控制(暂停/快进)
- 支持窗口尺寸动态调整
注意:需链接 FFmpeg 库(
avcodec,avformat,swscale)和 SDL2 库编译执行。
3万+

被折叠的 条评论
为什么被折叠?



