1

デスクトップ画面をキャプチャするために、Mac OS X の C++ プロジェクトに次のコードを実装しました。

int ScreenCaptureRoutines::CaptureImage(int width, int height)
{
  CGRect captureRect;

  captureRect.origin.x = 0;
  captureRect.origin.y = 0;
  captureRect.size.width = width;
  captureRect.size.height = height;

  CGImageRef img = CGWindowListCreateImage(captureRect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault);

  if(img == NULL)
  {
      fprintf(stderr, "CGWindowListCreateImage failed\n!");
      return -1;
  }

  /* get pixels */
  CGDataProviderRef provider = CGImageGetDataProvider(img);
  CFDataRef dataRef = CGDataProviderCopyData(provider);
  uint8_t* pixels = (uint8_t*)CFDataGetBytePtr(dataRef);

  BitmapUtility::SaveBitmapToFile(pixels, width, height, 32, "/Users/Main/test32.bmp");
}

このコードは、1024x768、1280x1024、1280x960、1280x768 などの解像度ではうまく機能しますが、1366x768 では画像が完全にスクランブルされます。

これは、ピクセルを保存するために使用される SaveBitmapFile メソッドです。

void BitmapUtility::SaveBitmapToFile(u8* pBitmapBits, long lWidth, long lHeight, unsigned short wBitsPerPixel, char* lpszFileName)
{
     BITMAPINFOHEADER bmpInfoHeader = {0};
     bmpInfoHeader.biSize = sizeBITMAPINFOHEADER;
     bmpInfoHeader.biBitCount = wBitsPerPixel;
     bmpInfoHeader.biClrImportant = 0;
     bmpInfoHeader.biClrUsed = 0;
     bmpInfoHeader.biCompression = BI_RGB;
     bmpInfoHeader.biHeight = lHeight;
     bmpInfoHeader.biWidth = lWidth;
     bmpInfoHeader.biPlanes = 1;
     bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel / 8);

     BITMAPFILEHEADER bfh = {0};
     bfh.bfType=0x4D42;
     bfh.bfOffBits = sizeBITMAPINFOHEADER + sizeBITMAPFILEHEADER;
     bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;

     //Creating bitmap file
     int fileDescriptor = open(lpszFileName, O_CREAT | O_WRONLY);
     if (fileDescriptor == -1)
     {
        //returning if error encountered when opening file
        return;
     }

     ByteBufferOutputStream bufferInfoHeader;

     bufferInfoHeader.write(&bmpInfoHeader.biSize, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biWidth, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biHeight, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biPlanes, 2);
     bufferInfoHeader.write(&bmpInfoHeader.biBitCount, 2);
     bufferInfoHeader.write(&bmpInfoHeader.biCompression, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biSizeImage, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biXPelsPerMeter, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biYPelsPerMeter, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biClrUsed, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biClrImportant, 4);

     ByteBufferOutputStream bufferFileHeader;

     bufferFileHeader.write(&bfh.bfType, 2);
     bufferFileHeader.write(&bfh.bfSize, 4);
     bufferFileHeader.write(&bfh.bfReserved1, 2);
     bufferFileHeader.write(&bfh.bfReserved2, 2);
     bufferFileHeader.write(&bfh.bfOffBits, 4);

     //Writing bitmap to file
     ssize_t bytesWritten = write(fileDescriptor, bufferFileHeader.get()->getData(), sizeBITMAPFILEHEADER);
     bytesWritten = write(fileDescriptor, bufferInfoHeader.get()->getData(), sizeBITMAPINFOHEADER);
     bytesWritten = write(fileDescriptor, pBitmapBits, bmpInfoHeader.biSizeImage);

     //Closing bitmap file
     close(fileDescriptor);
 }

SaveBitmapToFile メソッドはテストにのみ使用されます。実際のシナリオでは、キャプチャしたピクセルを libavcodec に渡して、フレームを FLV 形式に変換して保存しますが、最終結果は同じで、画像がスクランブルされます。

コードが 1366x768 (またはその他の非標準の 4:3 または 16:9 の解像度) で機能しない理由を知っている人はいますか?

4

1 に答える 1

3

まず、データが有効であることを確認するために、キャプチャの直後に CGImageRef を画面にレンダリングするようにしてください。

キャプチャされた bmp ファイルを見てください。イメージが疑似ランダム ノイズのように見える場合は、ピクセル形式が間違っています。CGImageGetWidthCGImageGetHeight、およびCGImageGetBitsPerPixel (およびCGImage.hの他の関数) を呼び出して、ピクセル形式が正しいことを確認する必要があります。

それでもいくつかのピクセルを確認できるが、行がずれている場合は、バイトストライドが間違っています。1024 と 1280 は、32、64、および 128 の倍数です。一方、1366 は、2 の除数の最大乗数は 2 です。ドライバーは通常、64 バイトにアラインされたピクセル ストライドを使用します。これは、共通のキャッシュ ライン サイズであるためです。ストライドは、幅 + ゼロ パディングと同じです。CGImageGetBytesPerRowを呼び出して、前の行の開始アドレスからバイト オフセットを取得し、次の行の開始アドレスを取得します。

ストライドで画像を書き込む擬似コード:

ptr = pixels
bytesPerRow = CGImageGetBytesPerRow()
bytesPerPixel = (CGImageGetBitsPerPixel() + 7) / 8

for row = 0 to height:
    write(ptr, width*bytesPerPixel)
    ptr += bytesPerRow
于 2013-06-30T11:27:57.730 に答える