晃了好多论坛了,没一个人具体做过这个东西或是能给出一点具体的解决方案.问题其实不麻烦我想做的工作是:用VC做一个程序每隔十秒钟截一次屏,截获的数据要保存成文件传到服务器上去.我遇到的问题是:1:截屏其实并不难,可当运行到GetDIBits时,CPU占用的特别高,整个屏幕会非常的慢,由于我要截获的屏幕上大部分情况下是在播放视频,所有截屏的时候会一顿一顿的,效果非常不好,
请问哪位用过其他的什么好办法同样可以截屏却不耗费那么大资源?或是分块截屏也行,但本人水平有限,确实不会实现...2:不知道做过截屏的朋友们遇到过没有,反正我是遇到了,就是把截下来的屏幕存成文件后,如果屏幕上此时正在放的视频,那么当你打开截下来的图片文件时,图片上的视频区域居然不是静止的,而是和屏幕上放的视频一起动!!!哪位遇到过,请多多指教这其中的原因.

解决方案 »

  1.   

    你能使用这种方式截获播放的视频的画面吗?
    你遇到的问题应该是你的程序处理有问题!我也写过截屏的程序,是用gdiplus来处理的!考虑网络传送的问题,建议最好将图片存为jpg格式,然后再传送!
      

  2.   

    可以分块截屏,就不影响机器性能,由于你是变化快的视频,可能出现图象错位。看看我的软件  海天屏幕广播
    www.software168.com
      

  3.   

    做过截屏的程序,不过不涉及视频流,主要是对一个电子书工具自动翻页并抓屏,大概一两秒一次吧,然后转存成JPG图片,电脑分辨率为1280*1024,CPU占用率并不高。我觉得楼主的问题出在代码里,用不着分块截屏。即然是图片文件,又怎么会是运动的?不知道楼主是用哪个函数进行截屏的,我用的方法是直接保存屏幕DC的方法,这和键盘上print screen键的实现方式一样,用这种方法是无法对如RealPlay或MediaPlayer等常用的播放器的播放画面进行截图的,它们都采用视频流的技术,只能用DirectShow等进行截图,像常用的播放器都有自己的截图工具菜单。所以涉及到视频的截图要用DirectShow,GDI是不行的。
      

  4.   

    谢谢大家了,我把代码贴出来,有兴趣的朋友可以试一下,一定要在桌面用media player放个视频,注意不要停止media player,也不要关闭截屏的程序,这时候打开已经保存的图片,大家会看到现象的,至于CPU占用率高的问题,我不知道是否和屏幕上有视频有关系,不过大家可以到网上去查查"GetDIBits cpu占用率",网上很多这样的文章,但几乎没有解决的.BOOL   SaveScreenToBitmap(char*   path)   
    {   
    CDC   dc;   
    dc.CreateDC("DISPLAY",NULL,NULL,NULL);    char filename[128];
    memset(filename,0,sizeof(filename)); int   nX = 0;
    int   nY = 0;
    int   Width=GetSystemMetrics(SM_CXSCREEN);   
    int   Height=GetSystemMetrics(SM_CYSCREEN);   CBitmap   bm;  
    bm.CreateCompatibleBitmap(&dc,Width,Height);   
    CDC   tdc;   
    tdc.CreateCompatibleDC(&dc);   
    CBitmap*pOld=tdc.SelectObject(&bm);   
    tdc.BitBlt(0,0,Width,Height,&dc,nX,nY,SRCCOPY);  
    tdc.SelectObject(pOld);   

    BITMAP   btm;   
    bm.GetBitmap(&btm);   
    DWORD   size=btm.bmWidthBytes*btm.bmHeight;   
    LPSTR   lpData=(LPSTR)GlobalAllocPtr(GPTR,size);    BITMAPINFOHEADER   bih;   
    bih.biBitCount=btm.bmBitsPixel;   
    bih.biClrImportant=0;   
    bih.biClrUsed=0;   
    bih.biCompression=0;   
    bih.biHeight=btm.bmHeight;   
    bih.biPlanes=1;   
    bih.biSize=sizeof(BITMAPINFOHEADER);   
    bih.biSizeImage=size;   
    bih.biWidth=btm.bmWidth;   
    bih.biXPelsPerMeter=0;   
    bih.biYPelsPerMeter=0;   

    GetDIBits(tdc,bm,0,bih.biHeight,lpData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);  
    BITMAPFILEHEADER   bfh;   
    bfh.bfReserved1=bfh.bfReserved2=0;   
    bfh.bfType=((WORD)('M'<<   8)|'B');   
    bfh.bfSize=54+size;   
    bfh.bfOffBits=54;    CFile   bf;   
    if(bf.Open(path,CFile::modeCreate|CFile::modeWrite)){   
    bf.WriteHuge(&bfh,sizeof(BITMAPFILEHEADER));   
    bf.WriteHuge(&bih,sizeof(BITMAPINFOHEADER));   
    bf.WriteHuge(lpData,size);   
    bf.Close();   
    }   
    else   
    return   FALSE;    GlobalFreePtr(lpData);    

    return   TRUE;   
    }
      

  5.   

    qqwwing(草莓猪),我也用的保存屏幕DC的方法,不过其实就象你说的,这种方法是不应该截获播放器的视频流的,不过我用以上的程序却发现确实可以截获!!!但是有个前提,在截屏之前要先用media player等播放器打开一个视频文件,播放一会儿之后再停止,这时在开始截屏程序,就可以截获视频流了,而且也不是象我说的是运动的画面,不过如果不进行用播放器打开并关闭一个视频而马上进行截屏操作的话,那样画面就是运动的.说实在的,我也不知道这是为什么???
      

  6.   

    既然GetDIBits占用资源,那就不用它好了,我的截屏程序里面没有用到这个函数,用的CreateDIBSection来得到位图数据指针,楼主可以参考一下。BITMAPINFObi;//信息头
    void *pBits=NULL;
    CRect rect;//客户区窗口
    GetClientRect(&rect);
    int nWidth=rect.right;
    int nHeight=rect.bottom;
    //填充信息头
    ZeroMemory(&bi,sizeof(bi));
    bi.bmiHeader.biSize=sizeof(bi.bmiHeader);
    bi.bmiHeader.biHeight=nHeight;
    bi.bmiHeader.biWidth=nWidth;
    bi.bmiHeader.biPlanes=1;
    bi.bmiHeader.biBitCount=24;
    bi.bmiHeader.biCompression=BI_RGB;
    bi.bmiHeader.biSizeImage=3*nWidth*nHeight;//拷贝客户区至内存DC
    CDC   dc;   
    dc.CreateDC("DISPLAY",NULL,NULL,NULL);  
    HDC hActiveDC=dc.m_hDC;
    HDC hActiveWndCompatibleDC=CreateCompatibleDC(hActiveDC);
    HDC hActiveWndCompactibleBitmap=CreateCompatibleBitmap(hActiveDC,rect.right,rect.bottom);
    SelectObject(hActiveWndCompatibleDC,hActiveWndCompactibleBitmap);//保存内存DC
    HDChBmpFileDC=CreateCompatibleDC(hActiveWndCompatibleDC);
    HBITMAPhBmpFileBitmap=CreateDIBSection(hActiveWndCompatibleDC,&bi,DIB_RGB_COLORS,&pBits,NULL,0);
    SelectObject(hBmpFileDC,hBmpFileBitmap);
    BitBlt(hBmpFileDC,0,0,nWidth,nHeight,hActiveWndCompatibleDC,0,0,SRCCOPY);CString tempFileName;//目标文件名
    HANDLEhFile=CreateFile(tempFileName,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile!=INVALID_HANDLE_VALUE)
    {
    DWORDdwRet=0;
    //填充文件头
    BITMAPFILEHEADERbmfHeader;
    ZeroMemory(&bmfHeader,sizeof(bmfHeader));
    bmfHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    bmfHeader.bfSize=bi.bmiHeader.biSizeImage+bmfHeader.bfOffBits;
    bmfHeader.bfType='MB';
    WriteFile(hFile,&bmfHeader,sizeof(bmfHeader),&dwRet,NULL);
    WriteFile(hFile,&bi.bmiHeader,sizeof(bi.bmiHeader),&dwRet,NULL);
    WriteFile(hFile,pBits,bi.bmiHeader.biSizeImage,&dwRet,NULL);
    CloseHandle(hFile);
    }
      

  7.   

    上面的代码有小问题,因为从前的代码是只抓取电子书窗口的那部分屏幕,不是全屏抓,所以GetClientRect(&rect);
    int nWidth=rect.right;
    int nHeight=rect.bottom;
    处存的是电子书程序的窗口宽和高,要全屏的话这里要写入屏幕的宽和高
      

  8.   

    我也刚刚发现,不过我觉得只要的是全屏的话CPU占用的资源也不会小的.
      

  9.   

    确实是这样的,看来CreateDIBSection和GetDIBits是一样的,小区域的截屏还可以,全屏的就不行了
      

  10.   

    dc.CreateDC("DISPLAY",NULL,NULL,NULL);When you no longer need the DC, call the DeleteDC function.
      

  11.   

    我截屏的时候那个电子书软件是最大化的,这样才能得到最好的图书保存效果。所以虽然我的程序并不是保存全部的屏幕DC,但是也只差一个任务栏,我的显示器分辨率为1280*1024,我存的图像分辨率为1280*986,高度上只差四十个像素,应该和全屏差不多了,而且我的程序里还包括一个将BMP转换为JPG的过程,这些都在一秒钟内完成,而CPU的占用率只有20%到30%,所以我怀疑是你的程序框架有问题,CPU占用率高并非函数引起。
      

  12.   

    其实我也没具体观察CPU占用有多少,只是根据视频的显示效果判断的,看来还是和播放视频有关系.看来这条路是走不通了.我正在尝试分块截屏,可就象shenming123(www.software168.com)说的,截到的图片拼接起来后由于变化快的视频,所以造成了图象错位,我的做法是比方说我分8次截屏,那么我调用了8次的截屏函数:
    for(int i=0;i<8;i++)
    {
        char filename[128];
        memset(filename,0,sizeof(filename));
        sprintf(filename,"monitor_%d.bmp",i)
        SaveScreenToBitmap(filename);
    }那么我的想法是能不能在SaveScreenToBitmap函数中先一次性取到屏幕的数据,然后在分次的调用GetDIBits一块一块的取数据呢?比方说:
    BOOL SaveScreenToBitmap(char *path)
    {
    ......
        先准备好屏幕的数据,然后:
        for(int i=0;i<8;i++)
        {
            GetDIBits();//不知道这儿该怎么写?
        }
    ......
    }
    这种想法有问题吗?能不能实现??
      

  13.   

    如果能实现不就可以解决分N次调用SaveScreenToBitmap然后在把图象拼到一起之后的图象错位的问题了吗?
      

  14.   

    同意  Mackz(在相互) 资源没有释放dc.CreateDC("DISPLAY",NULL,NULL,NULL);   
    bm.CreateCompatibleBitmap(&dc,Width,Height);
    这两个好像没有看到释放的
      

  15.   

    恩,确实是没有释放,我已经加上了DeleteDC(dc);DeleteObject(bm);
    可这也没解决任何问题啊
      

  16.   

    这个问题真的有这么难吗?qqwwing(草莓猪)说的有道理1。拷屏要多久,不就是BitBlt(hBmpFileDC,0,0,nWidth,nHeight,hActiveWndCompatibleDC,0,0,SRCCOPY);
    看来csdn也全军覆没了......
    那就只好覆没了
      

  17.   

    如果是nt系统的话可以试试这样
    开始->运行->dxdiag->显示->DirectDraw 加速(把这个禁用), 这样就可以用GDI来截图了,视频也可以正常播放。我用BitBlt做的截屏程序能成功截屏,按PrintScreen也有效。
    希望有用。
      

  18.   

    aj3423(a.j.):
    请问把DirectDraw 加速这个东西去掉,会不会影响视频播放的质量呢?
    您说的办法我这就去试
      

  19.   

    第二个问题是因为播放器使用了overlay
      

  20.   

    aj3423(a.j.) :
    您的办法解决了第二个问题,谢谢您了.就这么结贴了,第一个问题我还是决定分块截屏,虽然图象错位了,可总算是能截到了.我确实是没分了,可别嫌少啊,呵呵.
      

  21.   

    可以采用镜像显卡驱动萨,捕变化屏,几乎不耗cpu