AVIファイルライブラリ を用いたメモリ上のムービー再生

プロジェクトダウンロード(VC++2005)

【プログラム主要部分(AviPlayback.cpp)】


#include "stdafx.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <crtdbg.h>
#include <TCHAR.h>

#include <vfw.h>
#include <mmreg.h>
#include <msacm.h>



#pragma comment (lib, "msacm32.lib")
#pragma comment (lib, "winmm.lib")
#pragma comment (lib, "vfw32.lib")

//
//    ビデオ再生用データ
//
typedef struct _videothreaddata {
    HWND       hWnd;
    BOOL       bExit;
    PAVISTREAM lpVideoStream;
} VIDEOTHREADDATA, *LPVIDEOTHREADDATA;

//
//    Wave 再生用データ
//
typedef    struct {
    LPBYTE        lpData;
    WAVEHDR        waveHdr;
    HWAVEOUT    hwo;
    BOOL        bActive;
    PAVISTREAM    lpAudioStream;
    BOOL        bPlaying;
}    WAVETHREADDATA, *LPWAVETHREADDATA;

typedef struct _moviedata{
    CRITICAL_SECTION    m_csSema;
    PAVIFILE    lpAviFile;
    HANDLE        hVideoThread;
    BOOL        bLoop;
    VIDEOTHREADDATA vtd;
    WAVETHREADDATA    wtd;
} MOVIEDATA, *LPMOVIEDATA;


static DWORD WINAPI ThreadProc(LPVOID);
void CALLBACK wavePlaybackProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);

LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
HWND    g_hWnd = NULL;

static char * g_lpData = NULL;
static long    g_cfileSize;

LRESULT CALLBACK IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM lParam1, LPARAM lParam2);

//    オフスクリーン描画用バッファ
#define    DRAW_OFFSCREEN
#ifdef    DRAW_OFFSCREEN
HDC        g_hOffscreen = NULL;
#endif
MOVIEDATA    g_movieData;

void    StartPlayback(LPMOVIEDATA pMovieData, HWND hWnd){
    DWORD        dwSize;
    DWORD        dwThreadId;
    PAVIFILE     lpaf;
    WAVEFORMATEX wf;
    WAVETHREADDATA    *lpwtd= &pMovieData->wtd;
    VIDEOTHREADDATA    *lpvtd = &pMovieData->vtd;

    AVIFileInit();

    if (AVIFileOpen(&pMovieData->lpAviFile, TEXT("test.MEV+"), OF_READ, NULL) != 0)
        return;
    if (AVIFileGetStream(pMovieData->lpAviFile, &lpvtd->lpVideoStream, streamtypeVIDEO, 0) != 0)
        lpvtd->lpVideoStream = NULL;
    if (AVIFileGetStream(pMovieData->lpAviFile, &lpwtd->lpAudioStream, streamtypeAUDIO, 0) != 0)
        lpwtd->lpAudioStream = NULL;

    AVIFileRelease(pMovieData->lpAviFile);
    
    if (lpwtd->lpAudioStream != NULL){
        //    オーディオ変換
        {
            LONG           lStart, lEnd;
            LONG           lSize;
            LPBYTE         lpAviData;
            LPWAVEFORMATEX lpwfAvi;
            DWORD            numPCMSample;
            lStart = AVIStreamStart(lpwtd->lpAudioStream);
            lEnd   = lStart + AVIStreamLength(lpwtd->lpAudioStream);
            AVISTREAMINFO    asInfo;
            AVIStreamInfo(lpwtd->lpAudioStream, &asInfo, sizeof(AVISTREAMINFO));

            AVIStreamReadFormat(lpwtd->lpAudioStream, lStart, NULL, &lSize);
            lpwfAvi = (LPWAVEFORMATEX)new BYTE[lSize];
            AVIStreamReadFormat(lpwtd->lpAudioStream, lStart, lpwfAvi, &lSize);

            AVIStreamRead(lpwtd->lpAudioStream, lStart, lEnd, NULL, 0, &lSize, NULL);
            lpAviData = new BYTE[lSize];

            AVIStreamRead(lpwtd->lpAudioStream, lStart, lEnd, lpAviData, lSize, NULL, NULL);

            MMRESULT        mmr;
            HACMSTREAM      has;
            ACMSTREAMHEADER ash;
            DWORD    dwAviSize = lSize;

            wf.cbSize = sizeof(WAVEFORMATEX);
            wf.nSamplesPerSec = lpwfAvi->nSamplesPerSec;
            wf.wFormatTag = WAVE_FORMAT_PCM;
            if (lpwfAvi->wFormatTag == wf.wFormatTag){
                wf = *lpwfAvi;
            }else
                acmFormatSuggest(NULL, lpwfAvi, &wf, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG|ACM_FORMATSUGGESTF_NSAMPLESPERSEC);
            
            mmr = acmStreamOpen(&has, NULL, lpwfAvi, &wf, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME);
            if (mmr != 0) {
                if (mmr == ACMERR_NOTPOSSIBLE)
                    _RPT0(_CRT_WARN,TEXT("Error-acmStreamOpen\n"));
                return;
            }

            acmStreamSize(has, dwAviSize, &dwSize, ACM_STREAMSIZEF_SOURCE);
            lpwtd->lpData = new BYTE[dwSize];

            ZeroMemory((BYTE*)&ash,sizeof(ACMSTREAMHEADER));
            ash.cbStruct    = sizeof(ACMSTREAMHEADER);
            ash.pbSrc       = lpAviData;
            ash.cbSrcLength = dwAviSize;
            ash.pbDst       = lpwtd->lpData;
            ash.cbDstLength = dwSize;

            acmStreamPrepareHeader(has, &ash, 0);
            acmStreamConvert(has, &ash, 0);
            acmStreamUnprepareHeader(has, &ash, 0);
            
            acmStreamClose(has, 0);


            delete    lpwfAvi;
            delete    lpAviData;

        }

        waveOutOpen(&lpwtd->hwo, WAVE_MAPPER, &wf, (DWORD)wavePlaybackProc, (DWORD)pMovieData, CALLBACK_FUNCTION);
        lpwtd->bPlaying = true;
        lpwtd->bActive 
            = (lpwtd->lpAudioStream != NULL && lpwtd->hwo != NULL);

        lpwtd->waveHdr.lpData         = (LPSTR)lpwtd->lpData;
        lpwtd->waveHdr.dwFlags        = 0;
        lpwtd->waveHdr.dwBufferLength = dwSize;

        waveOutPrepareHeader(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR));
        
        waveOutWrite(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR));
    }
    if (lpvtd->lpVideoStream != NULL){
        lpvtd->hWnd  = hWnd;
        lpvtd->bExit = 0;
        pMovieData->hVideoThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, pMovieData, 0, &dwThreadId);
    }
}

void    StopPlayback(LPMOVIEDATA pMovieData){
    WAVETHREADDATA    *lpwtd= &pMovieData->wtd;
    VIDEOTHREADDATA    *lpvtd = &pMovieData->vtd;
    lpwtd->bActive = false;
    if (pMovieData->hVideoThread != NULL) {
        lpvtd->bExit = TRUE;
        WaitForSingleObject(pMovieData->hVideoThread, 1000);
        CloseHandle(pMovieData->hVideoThread);
        pMovieData->hVideoThread = NULL;
    }
        
    if (lpwtd->hwo != NULL) {
        waveOutReset(lpwtd->hwo);
        waveOutUnprepareHeader(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR));
        waveOutClose(lpwtd->hwo);
    }

    if (lpvtd->lpVideoStream != NULL)
        AVIStreamRelease(lpvtd->lpVideoStream);
    if (lpwtd->lpAudioStream != NULL)
        AVIStreamRelease(lpwtd->lpAudioStream);
    
    if (lpwtd->lpData != NULL){
        delete    lpwtd->lpData;
        lpwtd->lpData = NULL;
    }
    
    AVIFileExit();
}
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = (WNDPROC)WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor        = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = NULL;
    wcex.lpszClassName    = _T("Game");
    wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);
    RegisterClassEx(&wcex);

    ZeroMemory((BYTE*)&g_movieData,sizeof(MOVIEDATA));

    InitializeCriticalSection(&g_movieData.m_csSema);
    //    ファイル読み込み
    struct    _stat    status;
    if (0 == _tstat(_T("test.avi"),&status)){
        g_cfileSize = status.st_size;
        g_lpData = new char[g_cfileSize];
        FILE    *fp = fopen(_T("test.avi"),_T("rb"));
        if (fp != NULL){
            fread(g_lpData,g_cfileSize,1,fp);
            fclose(fp);
        }
        //    mci:カスタムIOProc のインストール
        mmioInstallIOProc(mmioFOURCC('M', 'E', 'V', ' '), (LPMMIOPROC)IOProc,
                            MMIO_INSTALLPROC | MMIO_GLOBALPROC);
    }
    HWND hWnd;
    hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wcex.lpszClassName,_T("Game"),
                WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
                CW_USEDEFAULT,0,640,480,NULL,NULL,hInstance,NULL);
    if( !hWnd )
        return FALSE;

    RECT    bounds,client;
    GetWindowRect(hWnd,&bounds);
    GetClientRect(hWnd,&client);
    MoveWindow(hWnd,bounds.left,bounds.top,
        640 * 2 - client.right,
        480 * 2 - client.bottom,
        false );

    g_hWnd = hWnd;
    SetWindowLong(hWnd,GWL_USERDATA,(DWORD)&g_movieData);
    ShowWindow( hWnd, nCmdShow );
    UpdateWindow( hWnd );

    if (g_lpData == NULL){
        DestroyWindow(hWnd);
    }

#ifdef    DRAW_OFFSCREEN
    HDC    dc = GetDC(hWnd);
    HBITMAP    hBitmap = CreateCompatibleBitmap(dc,640,480);
    g_hOffscreen = CreateCompatibleDC(dc);
    SelectObject(g_hOffscreen,hBitmap);
    ReleaseDC(hWnd,dc);
#endif
    g_movieData.bLoop = TRUE;
    StartPlayback(&g_movieData, hWnd);
    MSG        msg;
    while(true){
        if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){
            if(msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }else{
        }
    }

    EnterCriticalSection(&g_movieData.m_csSema);
    if (g_lpData != NULL){
        mmioInstallIOProc(mmioFOURCC('M', 'E', 'V', ' '), NULL,MMIO_REMOVEPROC);
        delete    g_lpData;
        g_lpData = NULL;
    }
    LeaveCriticalSection(&g_movieData.m_csSema);
#ifdef    DRAW_OFFSCREEN
    DeleteDC(g_hOffscreen);
    DeleteObject(hBitmap);
#endif
    DeleteCriticalSection(&g_movieData.m_csSema);
    return (int)msg.wParam;
}



//
//    関数:WndProc
//    説明:ウインドウに渡されたイベントのハンドラ
//
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    LPMOVIEDATA    lpMovieData;
    lpMovieData = (LPMOVIEDATA)GetWindowLong(hWnd,GWL_USERDATA);
    switch (message) {

    case WM_CREATE: 
        break;
    case WM_PAINT:
#ifdef    DRAW_OFFSCREEN
            if (g_hOffscreen != NULL){
                HDC    hDC;
                PAINTSTRUCT    paintStruct;
                hDC = BeginPaint(hWnd,&paintStruct);
                EnterCriticalSection(&lpMovieData->m_csSema);
                BitBlt(hDC,0,0,640,480,g_hOffscreen,0,0,SRCCOPY);
                LeaveCriticalSection(&lpMovieData->m_csSema);
                ::EndPaint(hWnd,&paintStruct);
            }
#endif
            return DefWindowProc(hWnd, message, wParam, lParam);            

    case WM_DESTROY:
        lpMovieData->bLoop = FALSE;
        StopPlayback(lpMovieData);
        PostQuitMessage(0);

        return 0;

    default:
        break;

    }

    return DefWindowProc(hWnd, message, wParam, lParam);

}

void CALLBACK wavePlaybackProc(
  HWAVEOUT hwo,     
  UINT uMsg,        
  DWORD dwInstance, 
  DWORD dwParam1,   
  DWORD dwParam2    
  ){
    MOVIEDATA *pMovieData = (MOVIEDATA*)dwInstance;
    WAVETHREADDATA    *pwtd = &pMovieData->wtd;
    switch(uMsg){
    case MM_WOM_DONE:
        pwtd->bPlaying = FALSE;
        break;
    case WOM_CLOSE:
        break;
  }
}

static DWORD WINAPI ThreadProc(LPVOID lp)
{
    HDC              hdc;
    LONG             i,lCurrent;
    LONG             lStart, lEnd;
    LONG             lSize;
    HWND             hWnd;
    LPBYTE           lpBits;
    HDRAWDIB         hdd;
    PAVISTREAM       lpAviStream;
    LPMOVIEDATA        lpMovieData = (LPMOVIEDATA)lp;
    LPVIDEOTHREADDATA     lpvtd = (LPVIDEOTHREADDATA)&(lpMovieData->vtd);
    AVISTREAMINFO    asi;
    BITMAPINFOHEADER bih;
    BOOL    bOffscreen = false;

    lpAviStream = lpvtd->lpVideoStream;
    hWnd = lpvtd->hWnd;
    
    lStart = AVIStreamStart(lpAviStream);
    DWORD    st = timeGetTime();
    DWORD    ct;
    lEnd   = lStart + AVIStreamLength(lpAviStream);
    lCurrent = lEnd+1;
    AVIStreamInfo(lpAviStream, &asi, sizeof(AVISTREAMINFO));

    hdd = DrawDibOpen();

    int    dxSrc, dySrc;
    lSize = sizeof(BITMAPINFOHEADER);
    AVIStreamReadFormat(lpAviStream, lStart, &bih, &lSize);
    dxSrc = bih.biWidth;
    dySrc = bih.biHeight;
#ifdef    DRAW_OFFSCREEN
    bOffscreen = DrawDibBegin(hdd,g_hOffscreen,dxSrc,dySrc,&bih,dxSrc,dySrc,DDF_BUFFER);
#else
    hdc = GetDC(hWnd);
    bOffscreen = DrawDibBegin(hdd,hdc,dxSrc,dySrc,&bih,dxSrc,dySrc,DDF_BUFFER);
    ReleaseDC(hWnd,hdc);
#endif
    while (!lpvtd->bExit) {
        while (true) {
            ct = timeGetTime() - st;
            i = AVIStreamTimeToSample(lpAviStream,(LONG)ct);
            if (i >= lEnd){
                i = lEnd;
                lCurrent = lEnd;
                if (lpMovieData->bLoop == FALSE)
                    break;
                LPWAVETHREADDATA lpwtd = &lpMovieData->wtd;    
                if (!lpwtd->bActive){
                    st = timeGetTime();
                }else{
                    //    音声再生中なら、終了を待つ
                    if (!lpwtd->bPlaying){
                        st = timeGetTime();
                        waveOutReset(lpwtd->hwo);
                        waveOutWrite(lpwtd->hwo, &lpwtd->waveHdr, sizeof(WAVEHDR));    
                    }
                }
            }

            if (lCurrent != i){
#ifdef    DRAW_OFFSCREEN
                hdc = g_hOffscreen;
#else
                hdc = GetDC(hWnd);
#endif                
                if (i > (lCurrent + 1)){
                    _RPT0(_CRT_WARN,_T("Frame Dropped\n"));
                    ++lCurrent;
                    for (int j = 0; j < 4 ; ++j){
                        //    up to x4
                        lSize = sizeof(BITMAPINFOHEADER);
                        AVIStreamReadFormat(lpAviStream, lCurrent, &bih, &lSize);

                        lpBits = new BYTE[ bih.biSizeImage ];
                        
                        AVIStreamRead(lpAviStream, lCurrent, 1, lpBits, bih.biSizeImage, NULL, NULL);
                        
                        EnterCriticalSection(&lpMovieData->m_csSema);
                        if (bOffscreen){
                            if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, DDF_DONTDRAW))
                                _RPT3(_CRT_WARN,_T("_DrawDibDraw failed. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc);
                            else
                                _RPT3(_CRT_WARN,_T("_DrawDibDraw succeeded. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc);

                        }else if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, 0)){
                            _RPT1(_CRT_WARN,_T("_DrawDibDraw failed. frame %d\n"),lCurrent);
                        }
                        LeaveCriticalSection(&lpMovieData->m_csSema);
                        
                        delete    lpBits;
                        if (i <= ++lCurrent)
                            break;
                    }
                }else{
                    lCurrent = i;
                }

                lSize = sizeof(BITMAPINFOHEADER);
                AVIStreamReadFormat(lpAviStream, lCurrent, &bih, &lSize);

                lpBits = new BYTE[bih.biSizeImage];

                AVIStreamRead(lpAviStream, lCurrent, 1, lpBits, bih.biSizeImage, NULL, NULL);

#if    1
                EnterCriticalSection(&lpMovieData->m_csSema);
                if (bOffscreen){
                    if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, DDF_DONTDRAW))
                        _RPT3(_CRT_WARN,_T("DrawDibDraw failed. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc);
                    else{
                        DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, DDF_UPDATE);
                        _RPT3(_CRT_WARN,_T("DrawDibDraw succeeded. frame %d,%d,%d\n"),lCurrent,dxSrc,dySrc);
                    }
                }else if (!DrawDibDraw(hdd, hdc, 0, 0, dxSrc, dySrc, &bih, lpBits, 0, 0, dxSrc, dySrc, 0)){
                    _RPT1(_CRT_WARN,_T("DrawDibDraw failed. frame %d\n"),lCurrent);
                }
                LeaveCriticalSection(&lpMovieData->m_csSema);
#else
                _RPT1(_CRT_WARN,_T("DrawDibDraw failed. frame %d\n"),lCurrent);
#endif
                delete    lpBits;
#ifdef    DRAW_OFFSCREEN
                InvalidateRect(hWnd,NULL,false);
#else
                ReleaseDC(hWnd, hdc);
#endif
            }
            if (lpvtd->bExit)
                break;
            Yield();
        }
    }
    
    DrawDibClose(hdd);

    return 0;
}


//
//    mmio: IOProc
//

LRESULT CALLBACK IOProc(LPMMIOINFO lpMMIOInfo, UINT uMessage, LPARAM
lParam1, LPARAM lParam2)
{
  static BOOL alreadyOpened = FALSE;

  switch (uMessage) {
  case MMIOM_OPEN:
      if (alreadyOpened)
          return 0;
      alreadyOpened = TRUE;

      lpMMIOInfo->lDiskOffset = 0;
      return 0;

   case MMIOM_CLOSE:
       return 0;

   case MMIOM_READ:{
            LPARAM    dataRead = 0;
               EnterCriticalSection(&g_movieData.m_csSema);
            if (g_lpData != NULL){
                dataRead = lParam2;
                memcpy((void *)lParam1, g_lpData+lpMMIOInfo->lDiskOffset, lParam2);
                lpMMIOInfo->lDiskOffset += lParam2;
                dataRead = lParam2;
            }
            LeaveCriticalSection(&g_movieData.m_csSema);
            return (dataRead);
        }
   case MMIOM_SEEK:
       switch (lParam2) {
       case SEEK_SET:
           lpMMIOInfo->lDiskOffset = lParam1;
           break;

       case SEEK_CUR:
           lpMMIOInfo->lDiskOffset += lParam1;
           break;

       case SEEK_END:
           lpMMIOInfo->lDiskOffset = g_cfileSize - lParam1;
           break;
       }
       return lpMMIOInfo->lDiskOffset;

   default:
       return -1;
   }
}

 

 AVIFile ライブラリを用いて、AVIファイルの再生を行っています。
 ここでも、mmioInstallIOProc関数を使って登録したカスタム入出力プロシージャを  使用しています。この仕組みはWindows マルチメディア API 共通で使えるようです。

 映像に関しては、Thread を一つ生成して、タイミングを見ながら1フレームずつ解凍させては表示させています。
 オーディオについては、先頭から末尾までまるごとPCMに変換して再生開始させています。

 オーディオ丸ごとPCM変換というのは長いムービーではちょっと問題ですね・・・・。

 というのは、 という問題がありますね・・・。

戻る