*ビデオ信号について
日本ではNTSC方式と呼ばれるものが一般的。市販のキャプチャボードを用いれば、これをPCに動画や静止画として取り込むことができる。このためのソフトはボードに付属するケースが多いが、自作も可能。
-[[テレビとビデオ信号(その1):http://www.orixrentec.co.jp/tmsite/know/know_tv1-38.html]]([[オリックス・レンテック:http://www.orixrentec.co.jp/]]の[[測定器玉手箱:http://www.orixrentec.co.jp/tmsite/]]より)
-[[テレビとビデオ信号(その2):http://www.orixrentec.co.jp/tmsite/know/know_tv2-39.html]]
-[[ビデオの基礎:http://www.dvdforum.gr.jp/Technology/03-1.html]]([[DVD Forum:http://www.dvdforum.gr.jp/]]の[[DVD ビデオ周辺技術講座:http://www.dvdforum.gr.jp/Technology/index.html]]より)

*自作キャプチャソフト(Windows)

習作なのでバグや恥ずかしい勘違い等のオンパレードなのでしょう。どなたかこの先読み進まれる方、この点に注意して下さい。

**何をするものか
キャプチャボードからビデオ信号を読み込み、静止画に保存する。Video For Windows という規格を用いているので、これに対応したデバイスならキャプチャボードやUSBカメラ等製品を問わず動作すると思われる(未確認)。Video For Windows は若干古めの規格で、時代的にはDirectShowの方が適切そうだったがややこしそうなのでやめた。

JPEG周りは[[Independent JPEG Group:http://www.ijg.org/]]のコードを使用した。

**本体
SDKで作成。
-[[実行ファイル(123KB):http://www.issp.u-tokyo.ac.jp/labs/spectroscopy/akiyama/itoh/data/hiCapture.exe]]
-[[プロジェクトのディレクトリのアーカイブ(286KB):http://www.issp.u-tokyo.ac.jp/labs/spectroscopy/akiyama/itoh/data/hiCapture.zip]]

起動し、デバイスと静止画保存フォーマットを選べばメインの画面となる。左が現在キャプチャしている画面で、右が静止画用。
-snap ... 現在のキャプチャ画面を静止画領域に移(写)す 
-save ... 静止画領域をファイルに保存ファイル名は「ID#_年月日_時分秒」+拡張子(#はキャプチャデバイスの識別番号) 
-snap&save ... snapしてsaveする 
-timer ... 決められた時間毎にsnap&save

**参考link
-[[ビデオキャプチャ:http://www.katto.comm.waseda.ac.jp/~katto/Class/GazoTokuron/code/videocapture.html]] (自作といいつつも、内容はほとんどここに依存しています)
-[[ビデオキャプチャー:http://laputa.cs.shinshu-u.ac.jp/~gtakano/prog1.html]]
-[[BMPファイルフォーマット:http://www.kk.iij4u.or.jp/~kondo/bmp/index.html]]
-[[VFWのキャプチャーで・・・:http://forums.belution.com/ja/vc/000/165/41s.shtml]]
-[[ビデオデータをキャプチャするには?:http://homepage1.nifty.com/MADIA/vb/vb_bbs/200401_04010125.html]]
-[[VB上でWebカメラを操作するには?:http://hpcgi1.nifty.com/MADIA/VBBBS/wwwlng.cgi?print+200401/04010041.txt]]

**メモ

***プレビューとオーバーレイ
違いが良くわからんが、
-プレビュー(capPreview) -> ハードウェアの中身を一旦システムが解釈してからクライアント領域に表示
-オーバーレイ(capOverlay) -> ハードウェアから直接?クライアント領域に描画

てなところ?全く自身がない。

capPreviewしておくと、常にcapSetCallbackOnFrameで指定されたコールバックが呼ばれる。
Previewの度に呼ばれるのだから当り前。
逆にcapOverlayだとそうはならない。ここでコールバックを呼ぶにはcapGrabFrameかcapGrabFrameNoStopを使う。
NoStopの方じゃないと、描画領域の更新が止まってしまう。


**ソース

 /////////////////////////////////////////////////////////////////////////////////////////
 // hiCapture - capture and save images (not video) via VFW (Video For Windows) device  //
 /////////////////////////////////////////////////////////////////////////////////////////
 //
 // Version 1.0 ('04 Jun, 30)
 // Hirotake Itoh <hiroitoh@issp.u-tokyo.ac.jp>
 //
 // NOTES:
 // * I'm no more than beginner and there may be lots bugs... use carefully.
 // * Of course, no warranty
 // * This program uses IJG works to deal with JPEG files. See "http://www.ijg.org/"
 //
 // REFERENCES:
 // http://www.katto.comm.waseda.ac.jp/~katto/Class/GazoTokuron/code/videocapture.html
 // http://black.sakura.ne.jp/~third/system/winapi/win.html
 // http://www7.plala.or.jp/keny01/win32/pre/index.html
 // http://www.kk.iij4u.or.jp/~kondo/bmp/index.html
 // http://www2m.biglobe.ne.jp/~yasutaka/
 //
 #include <windows.h>
 #include <stdio.h>
 #include <vfw.h>
 #include <commctrl.h>
 
 #include "jpeg/jpeglib.h"
 #include "resource.h"
 
 
 ////////////////
 // prototypes //
 ////////////////
 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // window procedure for the main window
 LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr); // callback for the video frame
 LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID, LPSTR lpErrorText);
 BOOL CALLBACK TimerDlgProc(HWND hTimerDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);    // dialog procedure (device selection)
 BOOL CALLBACK DeviceDlgProc(HWND hTimerDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);    // dialog procedure (timer)
 
 void ShowBitmap(HWND hWnd);    // show a captured frame in bitmap format
 int VideoInit(HWND hWnd);    // video initialization
 int VideoClose();            // video housekeepings
 
 int write_JPEG_file (char * filename, int quality);
 int write_BMP_file (char * filename);
 
 
 
 ////////////
 // macros //
 ////////////
 #define MYWINDOWCLASS "hiCapture"
 #define MYWINDOWTITLE "hiCapture - report bugs to: hiroitoh@issp.u-tokyo.ac.jp"
 #define MAX_CAPDEVICE 10    // limited to 10
 #define TIMERID 0    // can be any number
 
 
 
 //////////////////////
 // global variables //
 //////////////////////
 HINSTANCE hInst;        // this application instance
 HWND hWndVideo;            // window for the video frame
 HBITMAP hBitmapFrame;    // bitmap handle for the static image
 BITMAPINFO bmpInfo;        // bitmap information of the video frame
 BITMAPFILEHEADER bmfh;    // used when the program saves the bitmap
 int vWidth, vHeight;    // width and height (in pixel) of the video frame
 void *pPixelArray;        // pixel data of the grabbed frame
 char DeviceName[MAX_CAPDEVICE][100];    // device name
 BOOL IsTimerRunning = FALSE;    // used for timer
 UINT TimerInterval = 60;        // used for timer (in second)
 BOOL PreferJpeg = FALSE;    // format of the saved file
 
 
 
 //////////////////////////////////////
 // WinMain and main window settings //
 //////////////////////////////////////
 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR lpszCmdLine, int nCmdShow)
 {
     HWND hWnd;
     MSG msg;
     WNDCLASS myProg;
     hInst = hInstance;
     
     if (!hPreInst) {
         myProg.style            = CS_HREDRAW | CS_VREDRAW;
         myProg.lpfnWndProc        = WndProc;
         myProg.cbClsExtra        = 0;
         myProg.cbWndExtra        = 0;
         myProg.hInstance        = hInstance;
         myProg.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
         myProg.hCursor            = LoadCursor(NULL, IDC_ARROW);
         myProg.hbrBackground    = GetStockObject(WHITE_BRUSH);
         myProg.lpszMenuName        = MAKEINTRESOURCE(IDR_MENU1);
         myProg.lpszClassName    = MYWINDOWCLASS;
         if (!RegisterClass(&myProg))
             return FALSE;
     }
 
     hWnd = CreateWindow(
         MYWINDOWCLASS, MYWINDOWTITLE,    // class, title
         WS_OVERLAPPEDWINDOW,            // style
         CW_USEDEFAULT, CW_USEDEFAULT,    // horizontal and vertical positions
         CW_USEDEFAULT, CW_USEDEFAULT,    // width and height
         NULL, NULL,                        // parent, menu or child window identifiers
         hInstance,                        // application instance
         NULL                            // window creation data
         );
 
     ShowWindow(hWnd, nCmdShow);
     UpdateWindow(hWnd);
 
     while (GetMessage(&msg, NULL, 0, 0)) {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
     }
     return (msg.wParam);
 }
 
 
 
 ///////////////////////////
 // main window procedure //
 ///////////////////////////
 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     // error handling
     char errstr[100];
     int i;    // error code of VideoInit
 
     // window size
     RECT rWindow, rClient;
     int WidthOffset;  // (Window_region - Client_region).width
     int HeightOffset; // (Window_region - Client_region).height
     
     // file handling
     char filename[20];
     SYSTEMTIME stTime;
 
 
     switch (msg) {
     case WM_DESTROY:
         PostQuitMessage(0);
         break;
     case WM_CREATE:
         i = VideoInit(hWnd);
         if(i != 0) {
             wsprintf(errstr, "Error in \"VideoInit\" (%d)", i);
             MessageBox(hWnd, errstr, "error", MB_OK);
             PostQuitMessage(0);
         }
         if(IDYES == MessageBox(hWnd,
             "Choose the format for the image saving.\nYes -> JPEG\nNo -> BMP",
             "image format", MB_YESNO)) PreferJpeg = TRUE;
         SendMessage(hWnd, WM_SIZE, 0, 0);
         break;
     case WM_SIZE:
         GetWindowRect(hWnd, &rWindow);
         GetClientRect(hWnd, &rClient);
         WidthOffset = (rWindow.right - rWindow.left) - rClient.right;
         HeightOffset = (rWindow.bottom - rWindow.top) - rClient.bottom + 1;
 
         SetWindowPos(hWnd, NULL, 0, 0,
             vWidth*2+WidthOffset, vHeight+HeightOffset,    // width and height
             SWP_NOMOVE | SWP_NOZORDER);
         SetWindowPos(hWndVideo, NULL, 0, 0,
             vWidth, vHeight,                            // width and height
             SWP_NOMOVE | SWP_NOZORDER);
         break;
     case WM_CLOSE:
         VideoClose();
         DestroyWindow(hWnd);
         break;
     case WM_PAINT:
         ShowBitmap(hWnd);
         break;
     case WM_TIMER:
         SendMessage(hWnd, WM_COMMAND, (WPARAM)ID_SNAPANDSAVEIMAGE, 0);
         break;
     case WM_COMMAND:
 
         switch(LOWORD(wParam)){
 
         case ID_SNAP:
             capGrabFrameNoStop(hWndVideo);
             InvalidateRect(hWnd, NULL, FALSE);
             break;
         case ID_SAVEIMAGE:
             // time
             GetLocalTime(&stTime);
             wsprintf(filename, "%02d%02d%02d_%02d%02d%02d",
                stTime.wYear-2000, stTime.wMonth, stTime.wDay,
                stTime.wHour, stTime.wMinute, stTime.wSecond);
             if(PreferJpeg) write_JPEG_file(filename, 95);    // decrease '95' to save space
             else write_BMP_file(filename);
             break;
         case ID_SNAPANDSAVEIMAGE:
             SendMessage(hWnd, WM_COMMAND, (WPARAM)ID_SNAP, 0);
             SendMessage(hWnd, WM_COMMAND, (WPARAM)ID_SAVEIMAGE, 0);
             break;
         case ID_TIMER:
             // 'TimerInterval' is the interval in seconds.
             // 0 means that the user doesn't want to use the timer.
             TimerInterval = DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOGTIMER), NULL, TimerDlgProc);
             if(IsTimerRunning) {            // if the timer is running,
                KillTimer(hWnd, TIMERID);    // stop it,
                IsTimerRunning = FALSE;        // and update flag
             }
             if(TimerInterval != 0) {                                    // if the user wants to use the timer,
                SetTimer(hWnd, TIMERID, TimerInterval * 1000, NULL);    // start it,
                IsTimerRunning = TRUE;                                    // and update flag
             }
             break;
         }
 
         return 0;
         
     }
     return(DefWindowProc(hWnd, msg, wParam, lParam));
     
 }
 
 
 
 
 //////////////////////////////
 // dialog procedure (timer) //
 //////////////////////////////
 BOOL CALLBACK TimerDlgProc(HWND hTimerDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
     // retrieve window handles for each controls
     const HWND hWndTimerCheck = GetDlgItem(hTimerDlg, IDC_CHECKTIMER);
     const HWND hWndTimerEdit = GetDlgItem(hTimerDlg, IDC_EDITINTERVAL);
     const HWND hWndTimerSpin = GetDlgItem(hTimerDlg, IDC_SPININTERVAL);
     int nResult;    // timer interval in second
 
     switch(uMsg){
     case WM_INITDIALOG:
         SendMessage(hWndTimerSpin, UDM_SETRANGE, 0, MAKELONG(10800,1));    // interval range: 1s - 10800s (3h)
         SendMessage(hWndTimerSpin, UDM_SETPOS, 0, MAKELONG(TimerInterval,0));            // set previous interval
         if(IsTimerRunning) SendMessage(hWndTimerCheck, BM_SETCHECK, BST_CHECKED, 0);    // set previous flag
         else SendMessage(hWndTimerCheck, BM_SETCHECK, BST_UNCHECKED, 0);                //
         return TRUE;
     case WM_COMMAND:
 
         switch(LOWORD(wParam)){
 
         case IDOK:
             if(BST_UNCHECKED == SendMessage(hWndTimerCheck, BM_GETCHECK, 0, 0)) nResult = 0;
             else nResult = SendMessage(hWndTimerSpin, UDM_GETPOS, 0, 0);
             // now nResult is same as new interval, or 0, in case the user doesn't want to use the timer.
             EndDialog(hTimerDlg, nResult);
             break;
         default:
             return FALSE;
         }
 
     default:
         return FALSE;
     }
     return TRUE;
 }
 
 
 
 
 ///////////////////////////////
 // dialog procedure (device) //
 ///////////////////////////////
 BOOL CALLBACK DeviceDlgProc(HWND hDeviceDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
 
     const HWND hWndCombo = GetDlgItem(hDeviceDlg, IDC_COMBO1);
     int i;
     char tmpstr[100];
 
     switch(uMsg){
     case WM_INITDIALOG:
         // only valid devices are to be added to combobox
         for(i=0; i<MAX_CAPDEVICE; i++){
             if(0 != strcmp("none", DeviceName[i])){
                wsprintf(tmpstr, "id: %d, %s", i, DeviceName[i]);
                SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM)tmpstr);
             }
         }
         // anyway, select one
         SendMessage(hWndCombo, CB_SETCURSEL, 0, 0);
         return TRUE;
     case WM_COMMAND:
         switch(LOWORD(wParam)){
             case IDOK:
                // return device id#
                SendMessage(hWndCombo, WM_GETTEXT, sizeof(tmpstr), (LPARAM)tmpstr);
                sscanf(tmpstr, "id: %d,", &i);
                EndDialog(hDeviceDlg, i);
                break;
             default:
                return FALSE;
         }
     default:
         return FALSE;
     }
     return TRUE;
 }
 
 
 
 
 
 
 //////////////////////////
 // Video initialization //
 //////////////////////////
 int VideoInit(HWND hWnd){
 
     int i;    // used to retrieve indices of available drivers
     int j=0;    // check the number of valid drivers
     char DeviceVersion[100];    // almost for tempral use
     BOOL bDriverDescription;    // whether the specified driver is available or not
     DWORD wSize;            // the size of the video format
     char VideoFormat[200];    // used only for user notification
 
     // capture window
     hWndVideo = capCreateCaptureWindow(
         "CapWindow",
         WS_CHILD | WS_VISIBLE,
         0,
         0,
         320,
         240,
         hWnd,
         0
         );
     if(hWndVideo == NULL) return -1;
 
     // callbacks
     capSetCallbackOnFrame(hWndVideo, FrameCallbackProc);
     capSetCallbackOnError(hWndVideo, ErrorCallbackProc);
 
     // driver
     for(i=0; i<MAX_CAPDEVICE; i++){
         bDriverDescription = capGetDriverDescription(
             i,
             (LPSTR)DeviceName[i], sizeof(DeviceName[i]),
             (LPSTR)DeviceVersion, sizeof(DeviceVersion)
             );
         if(!bDriverDescription){
             wsprintf(DeviceName[i],"none");
             //wsprintf(DeviceMessage, "The following device was found:\n%s", DeviceName[i]);
             //MessageBox(hWnd, DeviceMessage, "Device Notification", MB_OK);
         }
         else j++;
     }
     if(j == 0) return -6;    // no valid drivers
     
     i = DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOGDEVICE), NULL, DeviceDlgProc);
     capDriverConnect(hWndVideo, i);
 
 
     // picture format
     wSize = capGetVideoFormatSize(hWndVideo);
     if( !capGetVideoFormat(hWndVideo, &bmpInfo, wSize) ){
         DestroyWindow(hWndVideo);
         return -2;
     }
     if( bmpInfo.bmiHeader.biCompression != BI_RGB || bmpInfo.bmiHeader.biBitCount != 24){
         MessageBox(hWnd, "Sorry, only 24bit RGB. exitting...", "error", MB_OK);
         return -3;
     }
     vWidth = bmpInfo.bmiHeader.biWidth;
     vHeight = bmpInfo.bmiHeader.biHeight;
     wsprintf(VideoFormat, "Width: %d\nHeight: %d", vWidth, vHeight);
     MessageBox(hWnd, VideoFormat, "Size:", MB_OK);
 
 
     // prepare bitmap file header
     bmfh.bfType = 'B' + ('M'<<8);    // 'BM'
     bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);    // offset to pixel array
     bmfh.bfSize = bmfh.bfOffBits + bmpInfo.bmiHeader.biSizeImage;    // entire filesize
 
 
     // create bitmap handle
     // NOTE: Memory allocation and deallocation of the array for pixels (pPixelArray)
     //       are automatically done by CreateDIBSection and DeleteObject. In other words,
     //       'malloc' or 'free' for pPixelArray must NOT be done manually.
     hBitmapFrame = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, &pPixelArray, NULL, 0);
     if(hBitmapFrame == NULL){
         MessageBox(hWnd, "\"CreateDIBSection\" failed", "error", MB_OK);
         return -4;
     }
 
 
     // show currently captured image
     capOverlay(hWndVideo, TRUE);
 
     return 0;
 
 }
 
 
 
 ////////////////////////
 // Video housekeeping //
 ////////////////////////
 int VideoClose(){
 
     // release callbacks
     capSetCallbackOnError(hWndVideo, NULL);
     capSetCallbackOnFrame(hWndVideo, NULL);
 
     // destroy the window
     DestroyWindow(hWndVideo);
 
     // release memory (i.e. bitmap handle)
     DeleteObject(hBitmapFrame);
 
     return TRUE;
 }
 
 
 
 
 ////////////////////////////
 // showing captured image //
 ////////////////////////////
 void ShowBitmap(HWND hWnd){
 
     HDC hDC, hBuffer; // hBuffer is used for memory device context, in order to show bitmap image
 
     hDC = GetDC(hWnd);
     hBuffer = CreateCompatibleDC(hDC);
     SelectObject(hBuffer, hBitmapFrame);
 
     BitBlt(
         hDC,        // target DC
         vWidth+1, 0,    // x, y coordinates of the target
         vWidth,        // width of the transfering rectangle
         vHeight,    // height of the transfering rectangle
         hBuffer,    // source DC
         0, 0,        // x, y coordinates of the source
         SRCCOPY        // raster operation mode
         );
     
     DeleteDC(hBuffer);
     ReleaseDC(hWnd, hDC);
     return;
 }
 
 
 
 
 ////////////////////////////////////////////////////////////////
 // frame callback function, which retrieves raw captured data //
 ////////////////////////////////////////////////////////////////
 LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr) 
 { 
     memcpy(pPixelArray, lpVHdr->lpData, lpVHdr->dwBufferLength);
     return (LRESULT) TRUE ; 
 } 
 
 
 
 /////////////////////////////
 // error callback function //
 /////////////////////////////
 LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID, LPSTR lpErrorText) 
 { 
     return (LRESULT) TRUE; 
 } 
 
 
 
 
 ///////////////////////
 // file saving (BMP) //
 ///////////////////////
 int write_BMP_file (char * filename){
     HANDLE hfile;
     char FilenameWithExt[256];
     DWORD tmp;
 
 
     wsprintf(FilenameWithExt, "%s.bmp", filename);
     hfile = CreateFile(FilenameWithExt, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
     WriteFile(hfile, &bmfh, sizeof(bmfh), &tmp, NULL);
     WriteFile(hfile, &bmpInfo.bmiHeader, sizeof(bmpInfo.bmiHeader), &tmp, NULL);
     SetFilePointer(hfile, bmfh.bfOffBits, NULL, FILE_BEGIN);
     GdiFlush();
     WriteFile(hfile, pPixelArray, bmpInfo.bmiHeader.biSizeImage, &tmp, NULL);
     CloseHandle(hfile);
     return 0;
 
 }
 
 
 ////////////////////////
 // file saving (JPEG) //
 ////////////////////////
 int write_JPEG_file (char * filename, int quality){
 
     // quality: 0(low)...100(high)  5-95 is useful range (from 'cjpeg.c')
     struct jpeg_compress_struct cinfo;
     struct jpeg_error_mgr jerr;
     FILE * outfile;        /* target file */
     JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
     int row_stride;        /* physical row width in image buffer */
     JSAMPLE * image_buffer;
     int i,j,k1,k2;
     unsigned char *temp;
     char FilenameWithExt[256];
 
     
     // allocate memory for image_buffer
     image_buffer = (JSAMPLE *)malloc(bmpInfo.bmiHeader.biSizeImage);
     if(image_buffer == NULL) return -1;
     
     //
     //                    | Windows BMP (see RGBQUAD) | what JPEG program wants |
     // -------------------+---------------------------+-------------------------+-
     //      color order   |   B,G,R,B,G,R,.......     |    R,G,B,R,G,B,........ |
     // -------------------+---------------------------+-------------------------+-
     //  vertical drawing  |  from bottom to top       |  from top to bottom     |
     // -------------------+---------------------------+-------------------------+-
     //
     // So we have to convert the pixel order of the source array to obtain nice JPEG image.
     // Otherwise, such as
     // memcpy(image_buffer, pPixelArray, bmpInfo.bmiHeader.biSizeImage);
     // a strange image will come.
 
     temp = (unsigned char *)pPixelArray;
 
 
     for(i=0; i< vHeight; i++) {
         k1 = i * 3 * vWidth;
         k2 = (vHeight - i - 1) * 3 * vWidth;
         for(j=0; j< 3*vWidth; j+=3) {
             *(image_buffer + k1 + j + 0) = *(temp + k2 + j + 2);
             *(image_buffer + k1 + j + 1) = *(temp + k2 + j + 1);
             *(image_buffer + k1 + j + 2) = *(temp + k2 + j + 0);
         }
     }
 
     // Step 1
     cinfo.err = jpeg_std_error(&jerr);
     jpeg_create_compress(&cinfo);
 
     // Step 2
     wsprintf(FilenameWithExt, "%s.jpg", filename);
     if ((outfile = fopen(FilenameWithExt, "wb")) == NULL) {
         fprintf(stderr, "can't open %s\n", FilenameWithExt);
         return -2;
     }
     jpeg_stdio_dest(&cinfo, outfile);
 
 
     // Step 3
     cinfo.image_width = vWidth;     /* image width and height, in pixels */
     cinfo.image_height = vHeight;
     cinfo.input_components = 3;        /* # of color components per pixel */
     cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
 
     jpeg_set_defaults(&cinfo);
     jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
 
     // Step 4
     jpeg_start_compress(&cinfo, TRUE);
 
     // Step 5
     row_stride = vWidth * 3;    /* JSAMPLEs per row in image_buffer */
 
     while (cinfo.next_scanline < cinfo.image_height) {
         row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
         (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
     }
 
     // Step 6
     jpeg_finish_compress(&cinfo);
     fclose(outfile);
 
     // Step 7
     jpeg_destroy_compress(&cinfo);
 
     // release image_buffer
     free(image_buffer);
     return 0;
 }

**リソーススクリプト

 //Microsoft Developer Studio generated resource script.
 //
 #include "resource.h"
 
 #define APSTUDIO_READONLY_SYMBOLS
 /////////////////////////////////////////////////////////////////////////////
 //
 // Generated from the TEXTINCLUDE 2 resource.
 //
 #include "afxres.h"
 
 /////////////////////////////////////////////////////////////////////////////
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
 // ニ?ワク?resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
 #ifdef _WIN32
 LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
 #pragma code_page(932)
 #endif //_WIN32
 
 /////////////////////////////////////////////////////////////////////////////
 //
 // Menu
 //
 
 IDR_MENU1 MENU DISCARDABLE
 BEGIN
     MENUITEM "snap",                        ID_SNAP
     MENUITEM "save",                        ID_SAVEIMAGE
     MENUITEM "snap&&save",                  ID_SNAPANDSAVEIMAGE
     MENUITEM "timer",                       ID_TIMER
 END
 
 
 #ifdef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
 //
 // TEXTINCLUDE
 //
 
 1 TEXTINCLUDE DISCARDABLE
 BEGIN
     "resource.h\0"
 END
 
 2 TEXTINCLUDE DISCARDABLE
 BEGIN
     "#include ""afxres.h""\r\n"
     "\0"
 END
 
 3 TEXTINCLUDE DISCARDABLE
 BEGIN
     "\r\n"
     "\0"
 END
 
 #endif    // APSTUDIO_INVOKED
 
 
 /////////////////////////////////////////////////////////////////////////////
 //
 // Icon
 //
 
 // Icon with lowest ID value placed first to ensure application icon
 // remains consistent on all systems.
 IDI_ICON1               ICON    DISCARDABLE     "icon1.ico"
 
 /////////////////////////////////////////////////////////////////////////////
 //
 // DESIGNINFO
 //
 
 #ifdef APSTUDIO_INVOKED
 GUIDELINES DESIGNINFO DISCARDABLE
 BEGIN
     IDD_DIALOGDEVICE, DIALOG
     BEGIN
  LEFTMARGIN, 7
  RIGHTMARGIN, 186
  TOPMARGIN, 7
  BOTTOMMARGIN, 39
     END
 
     IDD_DIALOGTIMER, DIALOG
     BEGIN
  LEFTMARGIN, 7
  RIGHTMARGIN, 129
  TOPMARGIN, 7
  BOTTOMMARGIN, 49
     END
 END
 #endif    // APSTUDIO_INVOKED
 
 
 /////////////////////////////////////////////////////////////////////////////
 //
 // Dialog
 //
 
 IDD_DIALOGDEVICE DIALOG DISCARDABLE  0, 0, 193, 46
 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Choose device "
 FONT 11, "Arial"
 BEGIN
     DEFPUSHBUTTON   "OK",IDOK,65,25,50,14
     COMBOBOX        IDC_COMBO1,7,7,179,79,CBS_DROPDOWNLIST | CBS_SORT
 |
      WS_VSCROLL | WS_TABSTOP
 END
 
 IDD_DIALOGTIMER DIALOG DISCARDABLE  0, 0, 136, 56
 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Timer setting"
 FONT 11, "Arial"
 BEGIN
     DEFPUSHBUTTON   "OK",IDOK,92,10,27,24
     CONTROL         "Timer",IDC_CHECKTIMER,"Button",BS_AUTOCHECKBOX |
      WS_TABSTOP,24,7,35,10
     LTEXT           "Interval [sec]",IDC_STATICTIMER,7,20,43,11
     EDITTEXT        IDC_EDITINTERVAL,7,32,71,12,ES_AUTOHSCROLL
     CONTROL         "Spin2",IDC_SPININTERVAL,"msctls_updown32",
      UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
      UDS_ARROWKEYS,63,31,11,14
 END
 
 #endif    // ニ?ワク?resources
 /////////////////////////////////////////////////////////////////////////////
 
 
 
 #ifndef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
 //
 // Generated from the TEXTINCLUDE 3 resource.
 //
 
 
 /////////////////////////////////////////////////////////////////////////////
 #endif    // not APSTUDIO_INVOKED