专栏名称: MzDavid
Android开发工程师
目录
相关文章推荐
蚂蚁摄影MaE  ·  P图前vsP图后,会修图的人也太太太“可怕”了! ·  13 小时前  
成都日报  ·  春“飞”入蓉城 ·  昨天  
成都日报  ·  春“飞”入蓉城 ·  昨天  
旅拍誌  ·  看 不 腻 的 胶 片 版 娜 比 ·  4 天前  
图虫APP  ·  OPENSEE一帧NO.1|在春天到来之前 ·  3 天前  
51好读  ›  专栏  ›  MzDavid

FFmpeg+SDL2实现简易音视频同步播放器

MzDavid  · 掘金  ·  · 2019-05-13 06:34

正文

阅读 11

FFmpeg+SDL2实现简易音视频同步播放器

SDL2文章列表

SDL2入门

SDL2事件处理

SDL2纹理渲染

SDL2音频播放

FFmpeg+SDL2实现视频流播放

FFmpeg+SDL2实现音频流播放

FFmpeg音视频同步

经过前面一系列的SDL2学习,终于到最后实现一个完整的简易播放器了。

线程模型

SimplePlayer

这是实现的简易播放器的线程模型,通过这张图再结合我们之前博客中学习的内容,基本可以了解播放器的一个整体运行流程。具体代码也是根据这张图来实现。

重要结构体

VideoState

整个播放器中最重要的结构体,解复用、解码、音视频同步、渲染相关参数都在该结构体中,它贯穿了整个播放流程。

typedef struct VideoState {
    
    char filename[1024]; // 文件名称
    AVFormatContext *pFormatCtx; // 上下文
    int videoStream, audioStream; //音视频流index


    //// 同步相关
    double audio_clock;
    double frame_timer;
    double frame_last_pts;
    double frame_last_delay;

    double video_clock; 
    double video_current_pts; 
    int64_t video_current_pts_time;  

    //音频相关
    AVStream *audio_st; // 音频流
    AVCodecContext *audio_ctx; // 音频解码上下文
    PacketQueue audioq; // 音频队列
    uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2]; // 音频缓存
    unsigned int audio_buf_size;
    unsigned int audio_buf_index;
    AVFrame audio_frame; // 音频帧
    AVPacket audio_pkt; // 音频包
    uint8_t *audio_pkt_data;
    int audio_pkt_size;
    struct SwrContext *audio_swr_ctx; // 音频重采样


    //video
    AVStream *video_st; // 视频流
    AVCodecContext *video_ctx; // 视频流解码上下文
    PacketQueue videoq; // 视频流队列


    VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; // 解码后视频帧数组
    int pictq_size, pictq_rindex, pictq_windex;
    SDL_mutex *pictq_mutex;
    SDL_cond *pictq_cond;

    SDL_Thread *parse_tid; // 解复用线程
    SDL_Thread *video_tid;// 视频解码线程

    int quit; // 退出标记位
} VideoState;
复制代码

PacketQueue

//// 解复用后音视频packet保存队列
typedef struct PacketQueue {
    AVPacketList *first_pkt, *last_pkt;
    int nb_packets;
    int size;
    SDL_mutex *mutex;
    SDL_cond *cond;
} PacketQueue;

复制代码

VideoPicture

//// 解码后视频帧
typedef struct VideoPicture {
    AVFrame *frame;
    int width, height;
    double pts; // 音视频同步后视频帧应该播放的时间
} VideoPicture;
复制代码

具体代码

Main

  1. 初始化
  2. 创建定时器,定时视频帧的刷新
  3. 创建解复用线程
  4. 等待事件
int WinMain(int argc, char *argv[]) {
    char *file = "C:\\Users\\lenovo\\Desktop\\IMG_5950.mp4";
    SDL_Event event;
    VideoState *is;
    is = av_mallocz(sizeof(VideoState));

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
        exit(1);
    }
    //创建SDL Window
    win = SDL_CreateWindow("Media Player",
                           100,
                           100,
                           640, 480,
                           SDL_WINDOW_RESIZABLE);
    if (!win) {
        fprintf(stderr, "SDL_CreateWindow error,exit!", SDL_GetError());
        exit(1);
    }

    renderer = SDL_CreateRenderer(win, -1, 0);
    text_mutex = SDL_CreateMutex();
    
    strlcpy(is->filename, file, sizeof(is->filename));
    is->pictq_mutex = SDL_CreateMutex();
    is->pictq_cond = SDL_CreateCond();

    // 定时刷新器,主要用来控制视频的刷新
    schedule_refresh(is, 40);

    // 创建解复用线程
    is->parse_tid = SDL_CreateThread(demux_thread, "demux_thread", is);
    if (!is->parse_tid) {
        av_free(is);
        return -1;
    }


    for (;;) {
        // 等待SDL事件,否则阻塞
        SDL_WaitEvent(&event);
        switch (event.type) {
            case FF_QUIT_EVENT:
            case SDL_QUIT: // 退出
                is->quit = 1;
                goto Destroy;
            case SDL_KEYDOWN:// ESC退出
                if (event.key.keysym.sym == SDLK_ESCAPE) {
                    is->quit = 1;
                    goto Destroy;
                }
                break;
            case FF_REFRESH_EVENT: // 定时器刷新事件
                video_refresh_timer(event.user.data1);
                break;
            default:
                break;
        }
    }

    // 退出
    Destroy:
    SDL_Quit();
    return 0;

}
复制代码

解复用

  1. 打开文件
  2. 找到音视频流
  3. 打开音频、视频流,创建视频解码线程,准备解码
  4. 读取packet,将音视频packet分别放入队列中,等待解码线程取出
int






请到「今天看啥」查看全文


推荐文章
成都日报  ·  春“飞”入蓉城
昨天
成都日报  ·  春“飞”入蓉城
昨天
旅拍誌  ·  看 不 腻 的 胶 片 版 娜 比
4 天前
图虫APP  ·  OPENSEE一帧NO.1|在春天到来之前
3 天前
悦读文摘  ·  真正伤害孩子的爸爸,其实是这5种
8 年前
中央戏剧学院就业创业指导中心  ·  招贤榜┇新疆艺术学院
7 年前
华夏影像诊断中心  ·  有了这张图,轻松诊断椎管内占位
7 年前