Microsoft DirectX 8.0

マルチメディア ストリーミングのサンプル コード

ここには、マルチメディア ストリーミング インターフェイスを実装するサンプル コードを記載する。ビデオ ストリーミングのサンプル コードは、ファイルを読み取り、それをプライマリ Microsoft® DirectDraw® サーフェスにレンダリングする方法を示す。このコードでは簡潔さのため、エラー チェックが省略されている。

2 番目のサンプル コードは、オーディオ ストリーミング インターフェイスを使ってオーディオ データをストリームする方法を示す。

以下の項で構成される。

ビデオ ストリーミングのサンプル コード

このサンプル コードでは、ファイルを読み取り、それをプライマリ DirectDraw サーフェスにレンダリングする。

#include <stdio.h>
#include "ddraw.h"       // DirectDraw インターフェイス
#include "mmstream.h"    // マルチメディア ストリーム インターフェイス
#include "amstream.h"    // DirectShow マルチメディア ストリーム インターフェイス
#include "ddstream.h"    // DirectDraw マルチメディア ストリーム インターフェイス



void RenderStreamToSurface(IDirectDrawSurface *pSurface, 
    IMultiMediaStream *pMMStream)
{    
    IMediaStream *pPrimaryVidStream;    
    IDirectDrawMediaStream *pDDStream;
    IDirectDrawStreamSample *pSample;
    RECT rect;
    DDSURFACEDESC ddsd;

    pMMStream->GetMediaStream(MSPID_PrimaryVideo, &pPrimaryVidStream);
    pPrimaryVidStream->QueryInterface(IID_IDirectDrawMediaStream, (void **)&pDDStream);
    ddsd.dwSize = sizeof(ddsd);
    pDDStream->GetFormat(&ddsd, NULL, NULL, NULL);
    rect.top = rect.left = 0;
    rect.bottom = ddsd.dwHeight;
    rect.right = ddsd.dwWidth;
    pDDStream->CreateSample(pSurface, &rect, 0, &pSample);

    pMMStream->SetState(STREAMSTATE_RUN);
    while (pSample->Update(0, NULL, NULL, NULL) == S_OK);
    pMMStream->SetState(STREAMSTATE_STOP);

    pSample->Release();    
    pDDStream->Release();
    pPrimaryVidStream->Release();
}

void RenderFileToMMStream(const char * szFileName, 
    IMultiMediaStream **ppMMStream,
    IDirectDraw *pDD)
{    
IAMMultiMediaStream *pAMStream;
CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
    IID_IAMMultiMediaStream, (void **)&pAMStream);
    WCHAR    wPath[MAX_PATH];        // ワイド (32 ビット) 文字列名
    MultiByteToWideChar(CP_ACP, 0, szFileName, -1, wPath,
        sizeof(wPath)/sizeof(wPath[0]));    

    pAMStream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
    pAMStream->AddMediaStream(pDD, &MSPID_PrimaryVideo, 0, NULL);
    pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, AMMSF_ADDDEFAULTRENDERER, NULL);
    pAMStream->OpenFile(wPath, 0);    
    *ppMMStream = pAMStream;
}

int _CRTAPI1 main(int argc, char *argv[])    
{    
    if (argc < 2) {
    printf("Usage : showstrm movie.ext\n");
    exit(0);}    

    DDSURFACEDESC ddsd;
    IDirectDraw *pDD;    
    IDirectDrawSurface *pPrimarySurface;
    IMultiMediaStream *pMMStream;

    CoInitialize(NULL);
    
    DirectDrawCreate(NULL, &pDD, NULL);
    pDD->SetCooperativeLevel(GetDesktopWindow(), DDSCL_NORMAL);
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    pDD->CreateSurface(&ddsd, &pPrimarySurface, NULL);
    RenderFileToMMStream(argv[1], &pMMStream, pDD);
    RenderStreamToSurface(pPrimarySurface, pMMStream);    
    pMMStream->Release();
    pPrimarySurface->Release();    
    pDD->Release(); 
    
CoUninitialize();
return 0;
}

オーディオ ストリーミングのサンプル コード

次のサンプル コードは、IAudioMediaStreamIAudioStreamSampleIMemoryData、および IAudioData の 4 つのインターフェイスを使ってオーディオ データをストリームする方法を示す。

#include <windows.h>
#include <mmsystem.h>
#include <amstream.h>

/********************************************************************

   簡単なウェーブ プレーヤー処理

 ********************************************************************/

class CWaveBuffer;

class CWaveBuffer {
public:
        CWaveBuffer();
        ~CWaveBuffer();
        BOOL Init(HWAVEOUT hWave, int Size);
        void Done();
        BOOL Write(PBYTE pData, int nBytes, int& BytesWritten);
void Flush();

private:
        WAVEHDR      m_Hdr;
        HWAVEOUT     m_hWave;
        int          m_nBytes;
};

class CWaveOut {
public:
        CWaveOut(LPCWAVEFORMATEX Format, int nBuffers, int BufferSize);
        ~CWaveOut();
        void Write(PBYTE Data, int nBytes);
void Flush();
        void Wait();
        void Reset();
private:
        const HANDLE   m_hSem;
        const int      m_nBuffers;
        int            m_CurrentBuffer;
        BOOL           m_NoBuffer;
        CWaveBuffer   *m_Hdrs;
        HWAVEOUT     m_hWave;
};

/*
    CWaveBuffer
*/

CWaveBuffer::CWaveBuffer()
{
}

BOOL CWaveBuffer::Init(HWAVEOUT hWave, int Size)
{
    m_hWave  = hWave;
    m_nBytes = 0;

    /*  バッファを割り当て、ヘッダーを初期化する。 */
    m_Hdr.lpData = (LPSTR)LocalAlloc(LMEM_FIXED, Size);
    if (m_Hdr.lpData == NULL) {
return FALSE;
    }
    m_Hdr.dwBufferLength  = Size;
    m_Hdr.dwBytesRecorded = 0;
    m_Hdr.dwUser = 0;
    m_Hdr.dwFlags = 0;
    m_Hdr.dwLoops = 0;
    m_Hdr.lpNext = 0;
    m_Hdr.reserved = 0;

    /*  準備をする。 */
    waveOutPrepareHeader(hWave, &m_Hdr, sizeof(WAVEHDR));

return TRUE;
}

CWaveBuffer::~CWaveBuffer() {
    if (m_Hdr.lpData) {
        waveOutUnprepareHeader(m_hWave, &m_Hdr, sizeof(WAVEHDR));
        LocalFree(m_Hdr.lpData);
    }
}

void CWaveBuffer::Flush()
{
    // ASSERT(m_nBytes != 0);
m_nBytes = 0;
waveOutWrite(m_hWave, &m_Hdr, sizeof(WAVEHDR));
}

BOOL CWaveBuffer::Write(PBYTE pData, int nBytes, int& BytesWritten)
{
    // ASSERT((DWORD)m_nBytes != m_Hdr.dwBufferLength);
    BytesWritten = min((int)m_Hdr.dwBufferLength - m_nBytes, nBytes);
    CopyMemory((PVOID)(m_Hdr.lpData + m_nBytes), (PVOID)pData, BytesWritten);
    m_nBytes += BytesWritten;
    if (m_nBytes == (int)m_Hdr.dwBufferLength) {
        /*  書き込む。 */
m_nBytes = 0;
waveOutWrite(m_hWave, &m_Hdr, sizeof(WAVEHDR));
return TRUE;
    }
return FALSE;
}

void CALLBACK WaveCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwUser, 
                           DWORD dw1, DWORD dw2)
{
    if (uMsg == WOM_DONE) {
        ReleaseSemaphore((HANDLE)dwUser, 1, NULL);
    }
}

/*
    CWaveOut
*/

CWaveOut::CWaveOut(LPCWAVEFORMATEX Format, int nBuffers, int BufferSize) :
    m_nBuffers(nBuffers),
    m_CurrentBuffer(0),
    m_NoBuffer(TRUE),
    m_hSem(CreateSemaphore(NULL, nBuffers, nBuffers, NULL)),
    m_Hdrs(new CWaveBuffer[nBuffers]),
    m_hWave(NULL)
{
    /*  ウェーブ デバイスを作成する。 */
    waveOutOpen(&m_hWave,
                WAVE_MAPPER,
                Format,
                (DWORD)WaveCallback,
                (DWORD)m_hSem,
                CALLBACK_FUNCTION);

    /*  ウェーブ バッファを初期化する。 */
    for (int i = 0; i < nBuffers; i++) {
        m_Hdrs[i].Init(m_hWave, BufferSize);
    }
}

CWaveOut::~CWaveOut()
{
    /*  まず、バッファをリセットする。 */
    waveOutReset(m_hWave);

    /*  バッファを解放する。 */
    delete [] m_Hdrs;

    /*  ウェーブ デバイスを閉じる。 */
    waveOutClose(m_hWave);

    /*  セマフォを解放する。 */
    CloseHandle(m_hSem);
}

void CWaveOut::Flush()
{
    if (!m_NoBuffer) {
        m_Hdrs[m_CurrentBuffer].Flush();
        m_NoBuffer = TRUE;
        m_CurrentBuffer = (m_CurrentBuffer + 1) % m_nBuffers;
    }
}

void CWaveOut::Reset()
{
waveOutReset(m_hWave);
}


void CWaveOut::Write(PBYTE pData, int nBytes)
{
    while (nBytes != 0) {
        /*  必要ならバッファを取得する。 */
        if (m_NoBuffer) {
WaitForSingleObject(m_hSem, INFINITE);
            m_NoBuffer = FALSE;
        }

        /*  バッファに書き込む。 */
        int nWritten;
        if (m_Hdrs[m_CurrentBuffer].Write(pData, nBytes, nWritten)) {
            m_NoBuffer = TRUE;
            m_CurrentBuffer = (m_CurrentBuffer + 1) % m_nBuffers;
            nBytes -= nWritten;
            pData += nWritten;
        } else {
            // ASSERT(nWritten == nBytes);
break;
        }
    }
}

void CWaveOut::Wait()
{
    /*  残っているバッファをすべて送る。 */
    Flush();

    /*  バッファが戻るまで待つ。 */
    for (int i = 0; i < m_nBuffers; i++) {
        WaitForSingleObject(m_hSem, INFINITE);
    }
    LONG lPrevCount;
    ReleaseSemaphore(m_hSem, m_nBuffers, &lPrevCount);
}

/************************************************************

  ウェーブ プレーヤー処理はここまで

 ************************************************************/


HRESULT RenderStreamToDevice(IMultiMediaStream *pMMStream)
{
    WAVEFORMATEX wfx;
    #define DATA_SIZE 5000
    PBYTE pBuffer = (PBYTE)LocalAlloc(LMEM_FIXED, DATA_SIZE);

    IMediaStream *pStream;
    IAudioStreamSample *pSample;
    IAudioMediaStream *pAudioStream;
    IAudioData *pAudioData;

    pMMStream->GetMediaStream(MSPID_PrimaryAudio, &pStream);
    pStream->QueryInterface(IID_IAudioMediaStream, (void **)&pAudioStream);
    pAudioStream->GetFormat(&wfx);
    CoCreateInstance(CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER,
                                    IID_IAudioData, (void **)&pAudioData);
    pAudioData->SetBuffer(DATA_SIZE, pBuffer, 0);
    pAudioData->SetFormat(&wfx);
    pAudioStream->CreateSample(pAudioData, 0, &pSample);
    HANDLE hEvent = CreateEvent(FALSE, NULL, NULL, FALSE);
    CWaveOut WaveOut(&wfx, 4, 2048);
    int iTimes;
    for (iTimes = 0; iTimes < 3; iTimes++) {
        DWORD dwStart = timeGetTime();
        for (; ; ) {
            HRESULT hr = pSample->Update(0, hEvent, NULL, 0);
            if (FAILED(hr) || MS_S_ENDOFSTREAM == hr) {
break;
            }
            WaitForSingleObject(hEvent, INFINITE);
            DWORD dwTimeDiff = timeGetTime() - dwStart;
            // 10 秒以内に限定する
            if (dwTimeDiff > 10000) {
break;
            }
            DWORD dwLength;
            pAudioData->GetInfo(NULL, NULL, &dwLength);
            WaveOut.Write(pBuffer, dwLength);
        }
        pMMStream->Seek(0);
    }

    pAudioData->Release();
    pSample->Release();    
    pStream->Release();
    pAudioStream->Release();
    LocalFree((HLOCAL)pBuffer);

return S_OK;
}

HRESULT RenderFileToMMStream(
    WCHAR * pszFileName, IMultiMediaStream **ppMMStream)
{
IAMMultiMediaStream *pAMStream;
CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
IID_IAMMultiMediaStream, (void **)&pAMStream);
pAMStream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
    pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL);
    pAMStream->OpenFile(pszFileName, AMMSF_RUN);
*ppMMStream = pAMStream;
return S_OK;
}

int _CRTAPI1 main(int argc, char *argv[])    
{
IMultiMediaStream *pMMStream;
CoInitialize(NULL);
    WCHAR wszName[1000];
    MultiByteToWideChar(CP_ACP, 0, argv[1], -1, wszName,
        sizeof(wszName) / sizeof(wszName[0]));
    RenderFileToMMStream(wszName, &pMMStream);
    RenderStreamToDevice(pMMStream);
pMMStream->Release();
CoUninitialize();
return 0;
}