dtplayer中关于sdl的集成

目录

1 概述

2 AVpicture的介绍

2 sdl的集成

3 sdl2的集成


1 概述


在原生的ffmpeg提供的播放器的例子ffplay中,audio和video的输出是依靠sdl或者sdl2来完成的,具体代码如下(来自ffplay.c):

static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, int64_t pos)

{

    VideoPicture *vp;

    int dst_pix_fmt;

    vp = &is->pictq[is->pictq_windex];


    /** alloc or resize hardware picture buffer */

    if (!vp->bmp ||

        vp->width != is->video_st->codec->width ||

        vp->height != is->video_st->codec->height) {


    /** if the frame is not skipped, then display it */

    if (vp->bmp) {

        AVPicture pict;


        /** get a pointer on the bitmap */

        SDL_LockYUVOverlay (vp->bmp);


        dst_pix_fmt = PIX_FMT_YUV420P;

        memset(&pict,0,sizeof(AVPicture));

        pict.data[0] = vp->bmp->pixels[0];

        pict.data[1] = vp->bmp->pixels[2];

        pict.data[2] = vp->bmp->pixels[1];


        pict.linesize[0] = vp->bmp->pitches[0];

        pict.linesize[1] = vp->bmp->pitches[2];

        pict.linesize[2] = vp->bmp->pitches[1];


        sws_flags = av_get_int(sws_opts, "sws_flags", NULL);

        is->img_convert_ctx = sws_getCachedContext(is->img_convert_ctx,

            vp->width, vp->height, vp->pix_fmt, vp->width, vp->height,

            dst_pix_fmt, sws_flags, NULL, NULL, NULL);

        if (is->img_convert_ctx == NULL) {

            fprintf(stderr, "Cannot initialize the conversion context\n");

            exit(1);

        }

        sws_scale(is->img_convert_ctx, src_frame->data, src_frame->linesize,

                  0, vp->height, pict.data, pict.linesize);


        /** update the bitmap content */

        SDL_UnlockYUVOverlay(vp->bmp);


        vp->pts = pts;

        vp->pos = pos;

    return 0;

}

这里只摘取了部分重要的代码,其中vp->bmp为SDL_Overlay,也就是输出缓冲区,读者可以认为将yuv数据拷贝到此缓冲区

就可以显示了,对于ffplay来讲是通过sws_scale来完成的


但很多用户希望直接通过将src_frame中对应的数据直接拷贝到SDL_Overlay来达到目的,

其中原因是多方面的,对于dtplayer来说就是在decoder的时候已经通过调用sws_scale转换好了,此处再转换一次本就属于浪费

更重要的是:若将sws_scale放在sdl输出中,那么就必须强制的依赖ffmpeg,这也是无法接受的。

下面就介绍dtplayer中是如何直接操作src_frame来实现video帧的显示


2 AVpicture的介绍


想要正确的显示YUV数据,需要对AVPicture数据结构有比较透彻的了解

特别是其data【4】和linesize【4】这两个成员数组

对于audio来讲,只用了data[0]和linesize【0】,比较简单,这里只介绍video部分

AVPicture里面有data[4]和linesize[4]其中data是一个指向指针的指针(二级、二维指针),也就是指向视频数据缓冲区的首地址

在实际的数据排列中,yuv是连续存放的,data数组中的成员可以认为是指向的是数据中的位移,具体如下图

data -->xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
             ^                          ^                            ^
              |                           |                              |
          data[0]                 data[1]                   data[2]

比如说,当pix_fmt=PIX_FMT_YUV420P时,data中的数据是按照YUV的格式存储的

也就是:

data -->YYYYYYYYYYYYYYUUUUUUUUUUUUUVVVVVVVVVVVV
             ^                          ^                               ^
              |                           |                                 |
          data[0]                 data[1]                     data[2]

linesize是指对应于每一行的大小,为什么需要这个变量,是因为在YUV格式和RGB格式时,每行的大小不一定等于图像的宽度。

       linesize = width + padding size(16+16) for YUV
       linesize = width*pixel_size  for RGB


padding is needed during Motion Estimation and Motion Compensation for Optimizing MV serach and  P/B frame reconstruction

for RGB only one channel is available
so RGB24 : data[0] = packet rgbrgbrgbrgb......
           linesize[0] = width*3
data[1],data[2],data[3],linesize[1],linesize[2],linesize[2] have no any means for RGB

 

测试如下:(原始的320×182视频)
如果pix_fmt=PIX_FMT_RGBA32
linesize 的只分别为:1280  0    0     0

如果pix_fmt=PIX_FMT_RGB24
linesize 的只分别为:960   0    0     0

如果pix_fmt=PIX_FMT_YUV420P
linesize 的只分别为:352   176  176   0

【注】以上内容参考:

http://blog.csdn.net/liaozc/article/details/6110474

http://bbs.chinavideo.org/viewthread.php?tid=119&extra=page%3D1%26filter%3Ddigest&page=1



2 sdl的集成


对于sdl version1.0来说

这里对于如何在dtplayer框架中添加vo不做介绍,感兴趣的自己读代码好了,本身逻辑也非常简单

直接贴出render的代码


static int vo_sdl_render (vo_wrapper_t *wrapper, AVPicture_t * pict){    dt_lock (&vo_mutex);    SDL_Rect rect;    SDL_LockYUVOverlay (overlay);    memcpy (overlay->pixels[0], pict->data[0], dw * dh);    memcpy (overlay->pixels[1], pict->data[2], dw * dh / 4);    memcpy (overlay->pixels[2], pict->data[1], dw * dh / 4);    SDL_UnlockYUVOverlay (overlay);    rect.x = dx;    rect.y = dy;    rect.w = dw;    rect.h = dh;    SDL_DisplayYUVOverlay (overlay, &rect);    dt_unlock (&vo_mutex);    return 0;}

可以看出对于AVpicture_t是封装的ffmpeg的AVPicture的结构,可以认为是没有区别的

操作就是将AVPicture的YUV缓冲区分别拷贝到 overlay的YUV缓冲区中,

需要注意的是这里拷贝的长度并非是AVpicture中的linesize【0】  linesize【1】  linesize【2】 

这里的原因解释下:之所以不能能直接使用linesize数组,是因为linesize【0】linesize【1】 linesize【2】

代表的并不是YUV数据的长度,而是每行多少像素个数+padding(参考上面第2小节),也就是说

linesize[0] = dw + 32

linesize[1] = dw/2

linesize[1] = dw/2

但data中保存的却是正确的yuv数据,因此我们只需要正确的设置要拷贝的数据量,就可以正确的显示视频了


3 sdl2的集成


sdl2与sdl1的接口差别很大,看下sdl2的代码

static int vo_sdl2_render (vo_wrapper_t *wrapper, AVPicture_t * pict)

{

    int ret = 0;

    sdl2_ctx_t *ctx = (sdl2_ctx_t *)wrapper->handle;

    

    if(!ctx->sdl_inited)

    {

        ret = sdl2_pre_init(ctx);

        ctx->sdl_inited = !ret;

    }

    

ctx->mux_vo.lock();

    SDL_Rect dst;

    dst.x = ctx->dx;

    dst.y = ctx->dy;

    dst.w = ctx->dw;

    dst.h = ctx->dh;

    if(ctx->ren == nullptr)

        ctx->ren = SDL_CreateRenderer(ctx->win,-1,0);

    if(ctx->tex == nullptr)

ctx->tex = SDL_CreateTexture(ctx->ren,SDL_PIXELFORMAT_YV12,SDL_TEXTUREACCESS_STREAMING,ctx->dw,ctx->dh);

    //ctx->tex = SDL_CreateTexture(ctx->ren,SDL_PIXELFORMAT_YV12,SDL_TEXTUREACCESS_STATIC,ctx->dw,ctx->dh);

    SDL_UpdateYUVTexture(ctx->tex,NULL, pict->data[0], pict->linesize[0],  pict->data[1], pict->linesize[1],  pict->data[2], pict->linesize[2]);

    //SDL_UpdateTexture(ctx->tex, &dst, pict->data[0], pict->linesize[0]);

    SDL_RenderClear(ctx->ren);

    SDL_RenderCopy(ctx->ren,ctx->tex,&dst,&dst);

    SDL_RenderPresent(ctx->ren);

    ctx->mux_vo.unlock();

    return 0;

}


sdl2的代码可以直接使用linesize数据的大小,是由于SDL_UpdateYUVTexture或者SDL_UpdateTexture本身直接需要的就是AVPicture中linesize的参数,看下接口说明

int SDL_UpdateYUVTexture(SDL_Texture*    texture,                         const SDL_Rect* rect,                         const Uint8 *   Yplane,                         int             Ypitch,                         const Uint8*    Uplane,                         int             Upitch,                         const Uint8*    Vplane,                         int             Vpitch)

Function Parameters

texture

the texture to update

rect

a pointer to the rectangle of pixels to update, or NULL to update the entire texture

Yplane

the raw pixel data for the Y plane

Ypitch

the number of bytes between rows of pixel data for the Y plane

Uplane

the raw pixel data for the U plane

Upitch

the number of bytes between rows of pixel data for the U plane

Vplane

the raw pixel data for the V plane

Vpitch

the number of bytes between rows of pixel data for the V plane


由于参数匹配,所以直接使用就可以了。



这里在使用SDL2的时候还有个地方需要注意:sdl2 是通过SDL_Window  SDL_Renderer  SDL_Texture三者协同作用来工作的

SDL_Window  ==窗口

SDL_Renderer ==画布

SDL_Texture    ==内容

这里窗口的创建必须在主线程中,至少要在显示输出的线程中创建,否则通过SDL_RenderPresent显示video的时候是不会刷新的

可以参考之前dtplayer的实现,由于后面将sdl2提前到player层面作为界面,因此可reset到之前版本,具体步骤如下:

git clone https://github.com/peterfuture/dtplayer.git

git reset   bcda0659a25f43f9f3e8eec9bb25d1b6c532281c

直接参考dtvideo/video_out/vo_sdl2.c就可以了 


github:https://github.com/avplayer/dtplayer   # C++

github:https://github.com/peterfuture/dtplayer # C

bug report: peter_future@outlook.com

blog: http://blog.csdn.net/dtplayer

bbs: http://avboost.com/

wiki: http://wiki.avplayer.org/Dtplayer


由于后面随着开发的进行文章会进行细节的更新,因此为了保证读者随时读到最新的内容,文章禁止转载,多谢大家支持!


转载于:https://my.oschina.net/u/1469860/blog/225363

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值