LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: jetking

今天把yuv的视频数据通过directfb显示出来了,一些心得和疑惑

[复制链接]
发表于 2007-7-3 20:52:25 | 显示全部楼层
如果楼主不太了解内核的驱动程序编程,那么就把压缩卡在内核态支持overlay的功能暂且忘记。

一个可行的方法时,用单线程的方式创建一个overlay表面,然后将压缩卡获取的yuv数据一个通道一个通道的copy过来,然后blit到主表面。获取压缩卡yuv数据的函数需要是nonblcoking,否则可能会产生相当的时延。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-7-4 22:57:45 | 显示全部楼层

看了一下以directfb为后端的SDL YUVOverlay的实现

在linux下做视频的一般会用到SDL,用SDL创建YUV overlay,显示时,一般遵循lock,copy ,unlock,display的过程。SDL支持许多video driver作为SDL_VIDEO的后端,默认为X11,还包括directfb,fbcon,svgalib等等其他的视频驱动。这几天想在 directfb下把视频显示出来,但是一直没找到好方法,修改了一下视频压缩卡供应商所提供的demo,以directfb为后端的SDL,把视频显示出来,于是就读了一下SDL中directfb的实现。

SDL中yuv overlay的操作一般有以下4个
CreateYUVOverlay
LockYUVOverlay
UnlockYUVovelay
DisplayYUVovelay

DirectFB_CreateYUVOverlay:
通过EnumDisplayLayer得到一个非primary的用于video的DisplayLayer,根据传进来的参数设置此layer的宽,高, pixelformat等等,然后把这些参数设置此layer(SetConfiguration),然后得到surface(GetSurface)。

DirectFB_LockYUVOverlay:
调用Lock来锁住surface,同时得到data地址和pitch,根据data和pitch和pixelformat来调整 SDL_overlayer的一些参数,包括overlayer->pixels. 用户在使用SDL时,就是拷贝数据至SDL_OverLayer对象的pixels处,其实pixels就是指向了Lock surface所得到的data地址。

DirectFB_UnlockYUVOverlay:
很简单,UnLock surface。

DirectFB_DisplayYUVOverlay
根据传进来的位置参数,调用layer->SetScreenLocation,用于显示。

后记:
今天用directfb做后端,SDL显示8路预览视频,占用cpu<40%;视频压缩卡的demo也是用SDL,只不过是在x11下,8路视频预览占cpu要80%~90%,据压缩卡供应商说装了显卡驱动会好很多,大概在40%左右。但我的directfb用的是vesafb驱动阿,效果就这么好了?有点不明。
理论上讲,只要按照SDL的方法来,也可以在directfb下实现YUV视频的显示,有时间的话我会再试试。
回复 支持 反对

使用道具 举报

发表于 2007-11-12 13:23:32 | 显示全部楼层
多通道的视频数据考到同一个overlay上,应该不能每个通道都lock吧。那样效率太低了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-11-12 16:04:33 | 显示全部楼层
不是同一个overlay,是每个所需预览的通道有一个各自的yuv overlay,如果是用sdl的话,yuv overlay是建立在sdl screen上的。

lock,unlock,display是应该是为了同步吧。
回复 支持 反对

使用道具 举报

发表于 2007-11-12 19:12:15 | 显示全部楼层
就算建n个overlay,但由于overlay都是建在同一个screen上,又由于Lock overlay的时候要lock screen。所以还是n个线程相互等待。效率是个问题。

另外jetking兄说的通过DirectFB的overlay方法,可以通过getsubsurface来获得overlay上的一部分,然后分别lock(如果可以到话),copy,display。应该在效率上要高很多(猜测,DirectFB还不是很熟)。

overlay是在显卡内存上,视频数据都是在系统内存中,估计拷贝起来也很慢。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-11-12 21:13:51 | 显示全部楼层
当时用了几种directfb的方法来显示,但是不知道什么原因,在用getsubsurface获取各通道的overlay,然后显示的方法,结果无法成功,画面凝固。也没深究下去。因为后来打算用gtk+sdl来做了。

在gtk+sdl做的视频预览上,安装n卡驱动,并启用显示硬件加速,16路预览,cpu占用率<10%,尽管理论上有springtime说的问题,但是实际上效率不差。
memcpy免不了,要把数据拷贝至显存(无论是物理的还是虚拟的)才能显示,但是数据量并不太大,因为,当16画面时,每个通道的预览数据大小只是该显示窗口大小的数据量,而不是原始预览数据流,当单画面时,数据量就大了,但是只有一个通道,所以总和起来也差不多。可能在某些捕捉卡下,预览数据流就是原始数据流了,这样就很大了,而我用的是hikvision的hc卡,没有这个问题,^_^

另外,之前我一直对用directfb显示视频,并加入gui控件的做法有点不明白,感觉这是挺复杂的事情,原来是很简单的,fb0用来显示视频,fb1用来做gui。以前一直想如何在一个fb上实现,太笨了。
回复 支持 反对

使用道具 举报

发表于 2007-11-13 14:07:33 | 显示全部楼层
cpu<10% 很不错了。你的16路预览是建立一个screen,16个线程,16个overlay,然后在每个线程里分别调用:lock screen , lock overlay,copy data, unlock overlay, unlock screen吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-11-14 10:30:55 | 显示全部楼层
线程里不lock screen,只lock overlay。

最主要是要装显卡驱动,不装的话,用xorg的默认的,cpu会在80~90%,而且查看进程,占cpu最多的,不是你的程序,而是xorg。
回复 支持 反对

使用道具 举报

发表于 2007-11-14 15:33:25 | 显示全部楼层

lock surface

Post by jetking;1782108
线程里不lock screen,只lock overlay。

最主要是要装显卡驱动,不装的话,用xorg的默认的,cpu会在80~90%,而且查看进程,占cpu最多的,不是你的程序,而是xorg。


按照上面的做法,线程里lock overlay。创建2个线程,在线程里分别创建overlay,lock,copy,unlock。
2个线程单独显示没问题。2个线程一起显示时,当第二个线程显示时,会把第一个线程显示的区域刷黑。就是说每次显示一个小区域,却会把整个screen都刷黑。这样的话假如16个线程同时显示就会在整个screen上看起来不停的闪。

代码:
#include <SDL/SDL.h>
#include <SDL/SDL_getenv.h>
#include <SDL/SDL_thread.h>
#include <stdio.h>
#include <memory.h>

typedef unsigned char BYTE;
       
#define WIDTH        800
#define HEIGHT        600

#define BUFLEN   WIDTH*HEIGHT*2

SDL_Surface *screen;

BYTE buf[BUFLEN];

////////////////////////////////////////////////////////////
//  thread1
//////////////////////////////////////////////////////////////
int thread1( void* data )
{       
        int arg = *(int*)data;

        printf( "arg: %d\n", arg );

        SDL_Overlay *yuv_overlay = SDL_CreateYUVOverlay(WIDTH/2,
                                                        HEIGHT/2,
                                                        SDL_YUY2_OVERLAY,
                                                        screen);


        if (yuv_overlay == NULL        ) {
                fprintf(stderr,
                        "SDL: Couldn't create SDL_yuv_overlay: %s\n",
                        SDL_GetError());
                return 0;
        }

#if 0
        /* Unlock SDL_yuv_overlay */
        if (SDL_MUSTLOCK(screen2)) {
                SDL_UnlockSurface(screen2);
        }
#endif

        if (SDL_LockYUVOverlay(yuv_overlay) < 0)
                return;

        //copy data to overlay
        //memcpy( yuv_overlay->pixels[0], buf, BUFLEN/4 );
        if ( 1 == arg ){
                memset( yuv_overlay->pixels[0], 255, BUFLEN/4 );
        }else if( 2 == arg ){
                memset( yuv_overlay->pixels[0], 0, BUFLEN/4 );
        }
#if 0
        /* Unlock SDL_yuv_overlay */
        if (SDL_MUSTLOCK(screen)) {
                SDL_UnlockSurface(screen);
        }
#endif
        SDL_UnlockYUVOverlay(yuv_overlay);

        SDL_Rect rect;
        if ( 1 == arg ){
                rect.x = 0;
                rect.y = 0;
                rect.w = WIDTH/2;
                rect.h = HEIGHT/2;
        }else if ( 2 == arg ){
                rect.x = WIDTH/2;
                rect.y = 0;
                rect.w = WIDTH/2;
                rect.h = HEIGHT/2;
        }

        SDL_DisplayYUVOverlay(yuv_overlay, &rect);
       
        SDL_Delay( 3000 );

        return 1;
}


////////////////////////////////////////////////////////////
//  main
//////////////////////////////////////////////////////////////
int main()
{       
        memset( buf, 255, BUFLEN );
       
        //SDL_Thread t1,t2;
        Uint32 t1,t2;

        printf( "sdl begin\n" );       

        SDL_Init(SDL_INIT_VIDEO);

        atexit(SDL_Quit);

        putenv( "SDL_VIDEO_WINDOW_POS = 0,0" );
        screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0,
                                  SDL_HWSURFACE);
       
        int arg;
        arg = 1;
        t1 = SDL_CreateThread( thread1, (void*)&arg );
        SDL_Delay(1000);
        int arg2 = 2;
        t2 = SDL_CreateThread( thread1, (void*)&arg2 );

        int status;
        SDL_WaitThread( t1, &status );
        SDL_WaitThread( t2, &status );

        return 0;
}
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-11-14 16:05:58 | 显示全部楼层
你两个通道overlay是一样的,都是SDL_Overlay *yuv_overlay,分成两个试试看,lock-copy-unlock-display 各自的yuv_overlay
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表