FFmpeg+SDL2 实现简易播放器:从视频解码到 YUV 渲染的完整流程

FFmpeg + SDL2 实现简易播放器:视频解码到 YUV 渲染流程

核心流程概述
  1. 初始化:加载视频文件,准备解码环境
  2. 解码:提取视频帧并解码为 YUV 数据
  3. 渲染:通过 SDL2 显示 YUV 帧
  4. 控制:实现播放状态管理

详细步骤与代码实现
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();


关键技术说明
  1. YUV 格式

    • 使用 AV_PIX_FMT_YUV420P 格式存储帧数据
    • YUV 分量分离:Y(亮度)、U/V(色度)
    • 内存布局:data[0]=Y, data[1]=U, data[2]=V
  2. 帧率控制

    • 通过 SDL_Delay() 实现简易帧率控制
    • 精确控制需计算 PTS(Presentation Time Stamp)
  3. 扩展建议

    • 添加音频解码与同步
    • 实现播放控制(暂停/快进)
    • 支持窗口尺寸动态调整

注意:需链接 FFmpeg 库(avcodec, avformat, swscale)和 SDL2 库编译执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值