Re[2]: Проблема с GDI
От: andrey.viktorov Россия  
Дата: 09.01.07 11:34
Оценка:
Мм... не совсем. Имеется граф, построенный почти руками. Имеется два ISampleGrabber'a (свой фильтр было лень писать, думаю обойтись этим) : один цепляется перед IFileSource'ом для выдирания собственно bitmap'ов (там же и ставится 24-х битность битмапа), другой цепляется перед Default Direct Sound Device и хватает PCM (по идее надо на концы посадить по Null Renderer'у, но тоже было лень). Есть два каллбака на каждый из потоков. Как только у нас приходит семлп, сразу вызывается процедура обработки видео\аудио и дальнейшей отсылки на сервер (есть нюанс такой : аудио отсылается ровно каждую секунду, а не по приходу семлпа, т.е. данные из семплов копятся, пока таймер не сработает). Вот аудио нормально шлётся и играется, а вот с видео... целыми днями с ним борюсь.

У нас имеется два каллбака :



////////////////////////////////////////////////
// Header
////////////////////////////////////////////////

class AudioCallback : public ISampleGrabberCB {
    private:
        // Data
        unsigned char pcmData[AUDIOBUFF];
        DWORD bufferSize;
        HANDLE eventHandler;
        int currentTime;
    public:
        // Constructors/Destructor
        inline AudioCallback() : bufferSize(0), currentTime(0) {}
        inline virtual ~AudioCallback() {}
        
        // Methods
            // Get methods
        inline unsigned char * getBuffer() { return &pcmData[0];}
        inline DWORD getBufferSize() const { return bufferSize;}
        inline void flushBuffer() { bufferSize = 0;}
            // Add event handler
        inline void addEventHandler(HANDLE ev) { eventHandler = ev;}
            // Audio callback
        HRESULT __stdcall BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen);

            // This method is not implemented
        inline HRESULT __stdcall SampleCB(double SampleTime, IMediaSample *pSample) {return S_OK;}
            // Fake out any COM reference counting
        inline ULONG __stdcall AddRef() {return 2;}
        inline ULONG __stdcall Release() {return 1;}
            // Fake out any COM QI'ing
        inline HRESULT __stdcall QueryInterface(const IID &riid, void **ppvObject) {
            if (NULL == ppvObject) return E_FAIL;
            if (riid == IID_ISampleGrabber || riid == IID_IUnknown) {
                *ppvObject = (void*)static_cast<ISampleGrabberCB*>(this);
                return S_OK;
            }
            return E_NOINTERFACE;
        }
};

class VideoCallback : public ISampleGrabberCB {
    private:
        // Data
        unsigned char * bmpData;
        long bufferSize;

        bmiEx bmpInfo;
        ISampleGrabber * pGrab;
        HANDLE eventHandler;
        HWND hOwner;
        
    public:
        // Constructors/Destructor
        inline VideoCallback() : bmpData(NULL), pGrab(NULL) {}
        inline virtual ~VideoCallback() {}

        // Methods
            // Get methods
        inline unsigned char * getBuffer() const { return bmpData;}
        inline long getBufferSize() const { return bufferSize;}
        inline BITMAPINFOHEADER* getBitmapInfo() { return (BITMAPINFOHEADER*)&bmpInfo;}
            // Add event handler
        inline void addEventHandler(HANDLE ev) { eventHandler = ev;}
            // Attach to ISampleGrabber interface
        inline void attach(ISampleGrabber * pGrabber) { pGrab = pGrabber;} 
            // Video callback
        HRESULT __stdcall BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen);
            //
        inline HRESULT __stdcall SampleCB(double SampleTime, IMediaSample *pSample) {return S_OK;}
            // Fake out any COM reference counting
        inline ULONG __stdcall AddRef() {return 0;}
        inline ULONG __stdcall Release() {return 0;}
            // Fake out any COM QI'ing
        inline HRESULT __stdcall QueryInterface(const IID &riid, void **ppvObject) {
            if (NULL == ppvObject) return E_FAIL;
            if (riid == IID_ISampleGrabber || riid == IID_IUnknown) {
                *ppvObject = (void*)static_cast<ISampleGrabberCB*>(this);
                return S_OK;
            }
            return E_NOINTERFACE;
        }
};

////////////////////////////////////////////////
// Cpp
////////////////////////////////////////////////


// Audio & Video callbacks
    // Audio
HRESULT AudioCallback::BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) {
    if (NULL == pBuffer) {
        return E_FAIL;
    }

//    SetEvent(eventHandler); -- этот евент срабатывает по таймеру
    
    if (bufferSize+BufferLen <= AUDIOBUFF) {
        memcpy((void*)&pcmData[bufferSize], (void*)pBuffer, BufferLen);
        bufferSize += BufferLen;
    }

    return S_OK;

}

HRESULT VideoCallback::BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) {
    if (NULL == pBuffer || NULL == pGrab) {
        return E_FAIL;
    }
    SetEvent(eventHandler);

    AM_MEDIA_TYPE type;
    pGrab->GetConnectedMediaType(&type);
    VIDEOINFOHEADER * vidHdr = (VIDEOINFOHEADER*)type.pbFormat;
    BITMAPINFOHEADER * header = (BITMAPINFOHEADER*)&vidHdr->bmiHeader;
    memcpy((void *)&bmpInfo, (void *)header, sizeof(BITMAPINFOHEADER));

    bmpData = pBuffer;
    bufferSize = BufferLen;

    return S_OK;
}


и имеется поток, который следит за приходом семплов (оба каллбака расположены в private-части класса FileCapture) :

void FileCapture::run() {
    Logger::debug("FileCapture::run - start");
    string errMsg;
    bool isStopOnError = false;
    while (finishRun != false) {
        HANDLE waiters[] = { videoProcessingWaiter, audioProcessingWaiter };    
        DWORD i;
        if ((i=WaitForMultipleObjects(2, waiters, FALSE, 0)) == WAIT_TIMEOUT) continue;   
        i = i - WAIT_OBJECT_0;

        if (listeners.IsEmpty()) {
            ResetEvent(waiters[i]);
            continue;
        }

        try {
            if (waiters[i] == videoProcessingWaiter) {
                processPendingVideo();
            }

            if (waiters[i] == audioProcessingWaiter) {
                processPendingAudio();
                audioCB.flushBuffer();
            }
        }catch(BaseException * e) {
            errMsg = e->getMessage();
            delete e;
            isStopOnError = true;
        }
        catch(std::bad_alloc&) {
            errMsg = "Out of memory";
            isStopOnError = true;
        }

        if(isStopOnError)
            break;

    }
    if(isStopOnError) {
        ::MessageBox(NULL, errMsg.c_str(), "Error", MB_OK | MB_ICONERROR);
        Logger::error("FileCapture::run - stop by reason: \"" + errMsg + "\"");
        FileCapture::destruct();
    }
    finishComplete = true;
    Logger::debug("FileCapture::run - done");
}


процедура обработки видео (немного оптимизировал, сделав bmpData массивом с фиксированной длиной и вынеся videoFmt за пределы функции, ибо он один раз должен инициализироваться) :

void FileCapture::processPendingVideo() {
    BITMAPINFOHEADER * srcInfo = videoCB.getBitmapInfo();
    unsigned char * srcData = videoCB.getBuffer();


    // Convert to compatible format (bitmap, no compression, 24bpp, 320x240)
    HDC dc = ::GetDC(NULL);
    HDC memDc = ::CreateCompatibleDC(dc);
    HBITMAP bmp = ::CreateCompatibleBitmap(dc, VIDEOCHAT_X, VIDEOCHAT_Y);
    ::ReleaseDC(NULL, dc);
    ::SelectObject(memDc, bmp);
    ::SetStretchBltMode(memDc, COLORONCOLOR);
    int x, y;
    int dx, dy;
    if (((double)srcInfo->biWidth / (double) srcInfo->biHeight) > (4.0/3.0)) {
        x = VIDEOCHAT_X;
        y = srcInfo->biHeight * (3.0/4.0);
        dx = 0;
        dy = (VIDEOCHAT_Y - y) / 2.0;
    }
    else {
        y = VIDEOCHAT_Y;
        x = srcInfo->biWidth * (4.0/3.0);
        dy = 0;
        dx = (VIDEOCHAT_X - x) / 2.0;
    }
    int res = ::StretchDIBits(memDc, dx, dy, x, y, 0, 0, srcInfo->biWidth, srcInfo->biHeight, srcData, (BITMAPINFO*)srcInfo, DIB_RGB_COLORS, SRCCOPY);

    HBITMAP curr = (HBITMAP)::SelectObject(memDc, NULL);

    int numLines = GetDIBits(memDc, bmp, 0, VIDEOCHAT_Y, &bmpData[0], (BITMAPINFO*)&videoFmt, DIB_RGB_COLORS); 

    ::DeleteBitmap(bmp);
    ::DeleteDC(memDc);
    // Send data to listeners
    ResetEvent(videoProcessingWaiter);
    POSITION p = listeners.GetHeadPosition();
    while (NULL != p) {
        CaptureListener * listener = listeners.GetNext(p);
        LOCK(listeners);
        
        listener->onVideoFrame(VIDEOCHAT_X, VIDEOCHAT_Y, (BYTE*)&bmpData[0]);
        UNLOCK(listeners);
    }
}

////////////////// Описание videoFmt ////////////
    videoFmt.biSize = sizeof(BITMAPINFOHEADER);
    videoFmt.biWidth = VIDEOCHAT_X;
    videoFmt.biHeight = VIDEOCHAT_Y;
    videoFmt.biPlanes = 1; 
    videoFmt.biBitCount = 24;
    videoFmt.biCompression = BI_RGB;
    videoFmt.biSizeImage = (((videoFmt.biWidth*videoFmt.biBitCount + 31)& ~31)/8)*(videoFmt.biHeight);
    videoFmt.biXPelsPerMeter = 0;
    videoFmt.biYPelsPerMeter = 0;
    videoFmt.biClrUsed = 0;
    videoFmt.biClrImportant = 0;


Звук обрабатывается аналогичным образом (конвертируется через acm в нужный формат и так же шлётся всем листенерам).

Т.е. по сути я не забочусь о синхронности: она должна обеспечиваться за счёт правильного поступления семплов, полученных из ISampleGrabber'ов. Если закомментировать код преобразования, то всё будет играть так, как надо.

По поводу DES. Можно поподробнее? Или ссылку на их сайт?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.