FFmpeg4.0+SDL2.0笔记02:Outputting to the Screen

2021-04-14 10:08:03 浏览数 (1)

环境

背景:在系统性学习FFmpeg时,发现官方推荐教程还是15年的,不少接口已经弃用,大版本也升了一级,所以在这里记录下FFmpeg4.0 SDL2.0的学习过程。

win10,VS2019,FFmpeg4.3.2,SDL2.0.14

原文地址:http://dranger.com/ffmpeg/tutorial02.html

SDL与Video

为了播放视频,我们将使用SDL库。SDL即Simple Direct Layer,它是非常优秀且跨平台的多媒体库,不少项目都有所使用。

SDL可以渲染多种图像格式,其中包括YUV格式。粗略讲一下YUV,与RGB类似,YUV是储存原始图像的一种格式,Y是亮度(luma),UV是色度。

SDL2.0现在支持7种YUV格式,包括ffmpeg解码出的YUV420P格式。420表示图像按4:2:0的比例采样,也就是每4个亮度样本(Y)对应1个色度样本(UV),总共6字节,相比RGB24需要4*3=12字节,YUV节省了一半带宽,并且人眼感知不到其中的差别。P表示YUV数据按planar格式存储——即Y,U,V分别存放在3个数组里。

首先来看看如何使用SDL库。第一步,初始化,注意main函数必须写成SDL库要求的形式int main(int argc, char* argv[]),否则编译不通过。另外SDL1.0里不少接口和组件已被弃用,参考:https://wiki.libsdl.org/MigrationGuide#SDL_1.2_to_2.0_Migration_Guide

代码语言:javascript复制
extern "C" {
#include <SDL.h>
#include <SDL_thread.h>
}

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        cout << "SDL_Init failed: " << SDL_GetError() << endl;
        return -1;
    }
}

创建窗口

SDL1.0里的SDL_SetVideoMode已被弃用,这里我们使用SDL_Window,SDL_Renderer和SDL_Texture,SDL_CreateWindow会创建对应宽高的窗口,SDL_CreateRenderer创建该窗口的渲染器,SDL_CreateTexture创建渲染器的Texture好让我们放置YUV数据。

代码语言:javascript复制
        SDL_Window* pScreen;
        SDL_Renderer* pRenderer;
        SDL_Texture* pTexture;
	    
	    //创建窗口
        pScreen = SDL_CreateWindow("SDLPlayer",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            pVideoCodecCtx->width,
            pVideoCodecCtx->height,
            0);
        if (pScreen == nullptr) {
            cout << "SDL_CreateWindow failed:" << SDL_GetError() << endl;
            return -1;
        }

        //创建渲染Context
        pRenderer = SDL_CreateRenderer(pScreen, -1, 0);
        if (pRenderer == nullptr) {
            cout << "SDL_CreateRenderer failed:" << SDL_GetError() << endl;
            return -1;
        }

        //初始化成白色
        SDL_SetRenderDrawColor(pRenderer, 255, 255, 255, 255);
        SDL_RenderClear(pRenderer);
        SDL_RenderPresent(pRenderer);

        //创建Render所需的Texture,用于放置YUV数据
        pTexture = SDL_CreateTexture(pRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pVideoCodecCtx->width, pVideoCodecCtx->height);
        if (pTexture == nullptr) {
            cout << "SDL_CreateTexture failed:" << SDL_GetError() << endl;
            return -1;
        }

渲染图像

渲染图像只需把YUV数据送给SDL_Texture,再送给SDL_Renderer,调用SDL_RenderPresent就行了。

代码语言:javascript复制
    int count = 0;
    //读取视频包并解码,转换,保存文件
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        if (packet.stream_index == iVideoStream) {
            avcodec_send_packet(pVideoCodecCtx, &packet);
            if (avcodec_receive_frame(pVideoCodecCtx, pFrame) == 0) {

                //将YUV更新至texture,然后渲染
                SDL_UpdateYUVTexture(pTexture, NULL,
                    pFrame->data[0], pFrame->linesize[0],      //Y
                    pFrame->data[1], pFrame->linesize[1],      //U
                    pFrame->data[2], pFrame->linesize[2]);     //V
                SDL_RenderClear(pRenderer);
                SDL_RenderCopy(pRenderer, pTexture, NULL, NULL);
                SDL_RenderPresent(pRenderer);
                cout << "presented frame" <<   count << endl;
            }
            //取消packet引用的内存,原先的av_free_packet弃用
            av_packet_unref(&packet);
        }
    }

运行程序便看到视频开始播放了!但是有个问题,整个视频像开了超级加速一样,没两下就播完了,后面我们会解决视频同步问题。

代码:https://github.com/onlyandonly/ffmpeg_sdl_player

0 人点赞