[SimplePlayer] 2. 在屏幕上显示视频图像

我们这里采用SDL(本文所用版本为SDL2.0.5)来进行图像输出,SDL在进行图像渲染时一般采用的会是direct3D或者opengl,SDL对它们进行了封装,不过我们这里只讨论SDL的使用,并不会去涉及这些底层实现。尽管如此,我们还是有必要了解其中一些基本概念,这些概念能帮助我们很好地理解所使用的SDL api的实际作用。

其中最重要的一个概念就是render,中文译为渲染,rendering在计算机图像领域指的就是图像合成(image synthesis)。通俗一点的解释就是:render指的是把2D图像(图形)或者3D模型变成所展示出来的图像的过程,实际进行这种转换操作的物件被称为renderer(渲染器)。在SDL中,renderer主要处理的是2D的数据,其中包括

  1. 图形:画点画线画面笔触上色填充颜色等。
  2. 图像:裁剪旋转等。

不过本文只用到renderer最基本的功能,即把图像输出到窗口。在SDL中,为了方便renderer的实现,renderer所处理的对象被规定为texture(纹理),因此图像需要先被包装成texture,texture会被render成rendered image,然后就可以输出到window。

image

 

 

初始化

在使用SDL的函数之前,需要先调用SDL_Init来进行初始化,由于本文的目的是进行视频图像输出,因此需要指定flag为SDL_INIT_VIDEO来初始化视频子系统。

    if(SDL_Init(SDL_INIT_VIDEO)){
        fprintf(stderr, "SDL init video failed\n");
        return -1;
    }

 

接下来创建window、renderer、texture。在创建texture时需要指定输入的图像像素格式,通常视频的像素格式都是yuv420p,对应的flag为SDL_PIXELFORMAT_IYUV。对于会频繁更换的视频图像,还需要指定texture为SDL_TEXTUREACCESS_STREAMING。

    window = SDL_CreateWindow("Simple Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DEF_WIDTH, DEF_HEIGHT, 0);
    if(!window){
        fprintf(stderr, "SDL create window failed\n");
        return -1;
    }
    
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if(!renderer){
        fprintf(stderr, "SDL create renderer failed\n");
        return -1;
    }

    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, DEF_WIDTH, DEF_HEIGHT);
    if(!renderer){
        fprintf(stderr, "SDL create renderer failed\n");
        return -1;
    }
    SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);

 

创建用于存储图像像素数据的缓冲区,并打开yuv文件,以供后续读取图像

    YPlane = (unsigned char *)malloc(DEF_WIDTH*DEF_HEIGHT);
    UPlane = (unsigned char *)malloc(DEF_WIDTH*DEF_HEIGHT/4);
    VPlane = (unsigned char *)malloc(DEF_WIDTH*DEF_HEIGHT/4);

    pFile = fopen(argv[1], "rb");
    if(pFile==NULL)
        return -1;

 

 

显示

循环读取图像进入缓冲区,然后把图像传入texture进行包装、渲染、最后输出,每帧间隔为1/24秒。对于视频子系统来说,还有一个必不可少的步骤,就是在循环中调用SDL_PumpEvents。SDL_PumpEvents如其名称,主要的功能是推动event队列以进行队列状态的更新,不过它还有一个作用是进行视频子系统的设备状态更新,如果不调用这个函数,所显示的视频会在大约10秒后丢失色彩。

    while(1){
        sizeY = fread(YPlane, 1, DEF_WIDTH*DEF_HEIGHT, pFile);
        sizeU = fread(UPlane, 1, DEF_WIDTH*DEF_HEIGHT/4, pFile);
        sizeV = fread(VPlane, 1, DEF_WIDTH*DEF_HEIGHT/4, pFile);
        if(!sizeY||!sizeU||!sizeV)
            break;
        if(0!=SDL_UpdateYUVTexture(texture, NULL, YPlane, DEF_WIDTH, UPlane, DEF_WIDTH/2, VPlane, DEF_WIDTH/2)){
            fprintf(stdout, "Render Update Texture failed, reason: %s\n", SDL_GetError());
        }
        SDL_RenderCopyEx(renderer, texture, NULL, NULL, 0, NULL, 0);
        SDL_RenderPresent(renderer);
        SDL_Delay(1000/FRAMERATE);

        SDL_PumpEvents();
    }

 

 

退出

退出前的收尾处理

    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    free(YPlane);
    free(UPlane);
    free(VPlane);
    fclose(pFile);
Android 系统中的 SimplePlayer.cpp 文件是一个简单的媒体播放器示例程序,它并不负责将文件编码成 MP4 格式。如果需要将一个视频文件编码成 MP4 格式,可以使用 Android 系统中提供的 MediaCodec 和 MediaMuxer 类来完成。具体的步骤如下: 1. 创建一个 MediaCodec 对象,并将其配置为编码器。可以使用 MediaCodec.createEncoderByType() 方法来创建编码器对象,然后使用 MediaCodec.configure() 方法进行配置,设置输出格式、编码器参数等参数。 2. 创建一个 MediaMuxer 对象,并调用其 addTrack() 方法添加一个音频或视频轨道。可以使用 MediaMuxer 构造函数创建一个对象,然后使用 addTrack() 方法添加轨道。 3. 开始编码和封装视频数据。使用 MediaCodec.start() 方法启动编码器,并调用 MediaCodec.getInputBuffer() 和 MediaCodec.getOutputBuffer() 方法获取输入和输出缓冲区。将视频数据写入输入缓冲区,然后将输出缓冲区中的数据写入 MediaMuxer 中。 4. 结束编码和封装过程,释放资源。在完成编码和封装过程后,需要调用 MediaCodec.stop() 和 MediaCodec.release() 方法释放编码器资源,调用 MediaMuxer.stop() 和 MediaMuxer.release() 方法释放 Muxer 资源,最后关闭文件输出流。 需要注意的是,编码和封装视频数据是一项相对复杂的任务,需要熟练掌握 Android 媒体编码、封装、解码等技术,以及相关的音视频编码标准和格式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值