mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
457 lines
10 KiB
457 lines
10 KiB
/*
|
|
* Microsoft YUV Codec
|
|
* hardware-assisted playback functions
|
|
*
|
|
* Copyright (c) Microsoft Corporation 1993
|
|
* All Rights Reserved
|
|
*
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <mmsystem.h>
|
|
#include <compddk.h>
|
|
|
|
#include "msyuv.h"
|
|
|
|
|
|
/*
|
|
* position and enable the overlay at pinst->rcDest (screen co-ords)
|
|
*/
|
|
DWORD
|
|
PlaceOverlay(PINSTINFO pinst)
|
|
{
|
|
DWORD mode;
|
|
OVERLAY_RECTS or;
|
|
RECT rc;
|
|
COLORREF cref;
|
|
RECT rcClient;
|
|
HDC hdc;
|
|
HBRUSH hbrOld;
|
|
|
|
/*
|
|
* check we have a device that supports overlay
|
|
*/
|
|
if((pinst->vh == NULL) ||
|
|
((mode = VC_GetOverlayMode(pinst->vh)) == 0)) {
|
|
return((DWORD) ICERR_ERROR);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* set the destination rect. This is the screen co-ords where
|
|
* the video should appear - and so is the overlay rect.
|
|
*/
|
|
or.ulCount = 1;
|
|
or.rcRects[0] = pinst->rcDest;
|
|
|
|
if (!VC_SetOverlayRect(pinst->vh, &or)) {
|
|
return( (DWORD) ICERR_ERROR);
|
|
}
|
|
|
|
/*
|
|
* set the overlay offset. this tells the board which pixel
|
|
* to place at the top-left of the overlay window. For us, this
|
|
* should always be pixel(0,0) of the frame buffer, so that whatever
|
|
* we draw to the framebuffer can go straight to the top left, and will
|
|
* appear correctly in the top left of the window
|
|
*/
|
|
SetRect(&rc, 0, 0, pinst->rcDest.right - pinst->rcDest.left,
|
|
pinst->rcDest.bottom - pinst->rcDest.top);
|
|
if (!VC_SetOverlayOffset(pinst->vh, &rc)) {
|
|
return( (DWORD) ICERR_ERROR);
|
|
}
|
|
|
|
|
|
|
|
/* init the overlay colour and brush if we haven't yet */
|
|
|
|
if (pinst->hKeyBrush == NULL) {
|
|
|
|
|
|
/*
|
|
* this version assumes a key-colour and simple rectangle
|
|
* combination
|
|
*/
|
|
ASSERT(mode & VCO_KEYCOLOUR);
|
|
ASSERT(mode & VCO_SIMPLE_RECT);
|
|
|
|
if (mode & VCO_KEYCOLOUR_FIXED) {
|
|
|
|
/* we need to get the key colour from the driver
|
|
* check first if we are getting rgb or palette index
|
|
*/
|
|
if (mode & VCO_KEYCOLOUR_RGB) {
|
|
cref = VC_GetKeyColour(pinst->vh);
|
|
} else {
|
|
cref = PALETTEINDEX(VC_GetKeyColour(pinst->vh));
|
|
}
|
|
} else {
|
|
/* we can set it ourselves. Check whether we should be setting
|
|
* an RGB or a palette index
|
|
*/
|
|
if (mode & VCO_KEYCOLOUR_RGB) {
|
|
RGBQUAD rgbq;
|
|
|
|
rgbq.rgbBlue = 0x7f;
|
|
rgbq.rgbGreen = 0;
|
|
rgbq.rgbRed = 0x7f;
|
|
VC_SetKeyColourRGB(pinst->vh, &rgbq);
|
|
|
|
cref = RGB(0x7f, 0, 0x7f);
|
|
|
|
} else {
|
|
|
|
VC_SetKeyColourPalIdx(pinst->vh, 5);
|
|
cref = PALETTEINDEX(5);
|
|
}
|
|
}
|
|
|
|
pinst->hKeyBrush = CreateSolidBrush(cref);
|
|
}
|
|
|
|
|
|
/* convert the screen co-ords for the overlay location into
|
|
* client window co-ords
|
|
*/
|
|
rcClient = pinst->rcDest;
|
|
MapWindowPoints(HWND_DESKTOP, pinst->hwnd, (PPOINT) &rcClient, 2);
|
|
|
|
|
|
/* paint the key colour over all the overlay area */
|
|
hdc = GetDC(pinst->hwnd);
|
|
hbrOld = SelectObject(hdc, pinst->hKeyBrush);
|
|
PatBlt(hdc, rcClient.left, rcClient.top,
|
|
rcClient.right - rcClient.left,
|
|
rcClient.bottom - rcClient.top,
|
|
PATCOPY);
|
|
SelectObject(hdc, hbrOld);
|
|
ReleaseDC(pinst->hwnd, hdc);
|
|
|
|
/* switch on overlay */
|
|
VC_Overlay(pinst->vh, TRUE);
|
|
|
|
|
|
return(ICERR_OK);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* check whether we can do this drawing or not
|
|
*/
|
|
DWORD
|
|
DrawQuery(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
|
|
{
|
|
VCUSER_HANDLE vh;
|
|
|
|
/* check that the input is our format */
|
|
if ((lpbiIn->biCompression != FOURCC_YUV411) ||
|
|
(lpbiIn->biBitCount != 16)) {
|
|
return( (DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
|
|
/*
|
|
* check 1:1 - we don't stretch (if we are given a output format)
|
|
*/
|
|
if (lpbiOut != NULL) {
|
|
if ((lpbiIn->biWidth != lpbiOut->biWidth) ||
|
|
(lpbiIn->biHeight != lpbiOut->biHeight)) {
|
|
return((DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* check we can open the device (if we haven't already done this)
|
|
*/
|
|
if (pinst->vh == NULL) {
|
|
|
|
int i;
|
|
|
|
/* for now, since there is no easy way of telling how
|
|
* many devices exist (and they may not be contiguous - and
|
|
* we cannot distinguish failure to exist from being busy),
|
|
* we will try all of the first 16 devices to see if any
|
|
* are available for overlaying
|
|
*/
|
|
for (i = 0; i < 16; i++) {
|
|
if ((vh = VC_OpenDevice(NULL, i)) != NULL) {
|
|
|
|
/* check this device can overlay */
|
|
if ((VC_GetOverlayMode(vh) & VCO_CAN_DRAW) != 0) {
|
|
|
|
/* found a good device */
|
|
break;
|
|
}
|
|
|
|
// no draw support - close and try next
|
|
VC_CloseDevice(vh);
|
|
vh = NULL;
|
|
}
|
|
}
|
|
if (vh == NULL) {
|
|
// we failed to find a device
|
|
return((DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
|
|
/* that's it - we can do it */
|
|
VC_CloseDevice(vh);
|
|
}
|
|
|
|
return(ICERR_OK);
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* initiate decompress&draw
|
|
*
|
|
* check that the input and output formats and sizes are valid, and
|
|
* that we can access the hardware.
|
|
*
|
|
* enable the overlay in the correct position
|
|
*
|
|
* Note that draw-begin and draw-end are not necessarily issued one-for-one,
|
|
* so the device may well still be open at this call.
|
|
*/
|
|
DWORD
|
|
DrawBegin(
|
|
PINSTINFO pinst,
|
|
ICDRAWBEGIN * icinfo,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
|
|
/*
|
|
* check that this is our format
|
|
*/
|
|
if ((icinfo->lpbi->biCompression != FOURCC_YUV411) ||
|
|
(icinfo->lpbi->biBitCount != 16)) {
|
|
return((DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
|
|
|
|
if (icinfo->dwFlags & ICDRAW_FULLSCREEN) {
|
|
return((DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
|
|
/*
|
|
* check 1:1 (we don't stretch)
|
|
*/
|
|
if ((icinfo->dxDst != icinfo->dxSrc) ||
|
|
(icinfo->dyDst != icinfo->dySrc)) {
|
|
return((DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
|
|
|
|
/*
|
|
* check we can open the device - if we haven't already done this.
|
|
*/
|
|
|
|
if (pinst->vh == NULL) {
|
|
|
|
int i;
|
|
|
|
/* for now, since there is no easy way of telling how
|
|
* many devices exist (and they may not be contiguous - and
|
|
* we cannot distinguish failure to exist from being busy),
|
|
* we will try all of the first 16 devices to see if any
|
|
* are available for overlaying
|
|
*/
|
|
for (i = 0; i < 16; i++) {
|
|
if ((pinst->vh = VC_OpenDevice(NULL, i)) != NULL) {
|
|
|
|
/* check this device can overlay */
|
|
if ((VC_GetOverlayMode(pinst->vh) & VCO_CAN_DRAW) != 0) {
|
|
|
|
/* found a good device */
|
|
break;
|
|
}
|
|
|
|
// no draw support - close and try next
|
|
VC_CloseDevice(pinst->vh);
|
|
pinst->vh = NULL;
|
|
}
|
|
}
|
|
|
|
if (pinst->vh == NULL) {
|
|
// we failed to find a device
|
|
return((DWORD) ICERR_UNSUPPORTED);
|
|
}
|
|
|
|
/* if this was a query - that's it. remember to close the device*/
|
|
if (icinfo->dwFlags & ICDRAW_QUERY) {
|
|
VC_CloseDevice(pinst->vh);
|
|
pinst->vh = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* we have checked all we need to check for a query. Don't close
|
|
* the device though, unless we just opened it for this query.
|
|
*/
|
|
if (icinfo->dwFlags & ICDRAW_QUERY) {
|
|
return(ICERR_OK);
|
|
}
|
|
|
|
|
|
/*
|
|
* remember the bits we will need later
|
|
*/
|
|
|
|
/*
|
|
* client window to draw into.
|
|
*
|
|
* Note that we also need a DC to paint the key-colour with. We should
|
|
* not use the DC passed with this message, as it will not remain valid
|
|
* (eg after a draw-end, we will still need a dc in processing draw-window).
|
|
* One alternative is to store the dc passed here, and to replace it with
|
|
* the dc passed with a draw-realize message. A cleaner solution (adopted
|
|
* here) is to get our own dc each time we need it.
|
|
*/
|
|
pinst->hwnd = icinfo->hwnd;
|
|
|
|
/*
|
|
* this is the portion of the original dib that we are to draw
|
|
*/
|
|
SetRect(&pinst->rcSource,
|
|
icinfo->xSrc,
|
|
icinfo->ySrc,
|
|
icinfo->dxSrc + icinfo->xSrc,
|
|
icinfo->dySrc + icinfo->ySrc);
|
|
|
|
/*
|
|
* this is the location (in window co-ords) within the client window
|
|
* where the video is to appear.
|
|
*/
|
|
SetRect(&pinst->rcDest,
|
|
icinfo->xDst,
|
|
icinfo->yDst,
|
|
icinfo->dxDst + icinfo->xDst,
|
|
icinfo->dyDst + icinfo->yDst);
|
|
/*
|
|
* we need to convert the rcDest from window-based to screen-based before
|
|
* writing to the hardware.
|
|
*/
|
|
MapWindowPoints(pinst->hwnd, HWND_DESKTOP, (PPOINT) &pinst->rcDest, 2);
|
|
|
|
|
|
/*
|
|
* enable and position the overlay
|
|
*/
|
|
return(PlaceOverlay(pinst));
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* decompress and render a single frame. Note that if we are pre-buffering,
|
|
* (which we don't in this driver), we should not start rendering frames
|
|
* until the draw-start message. As we don't pre-buffer (we don't respond
|
|
* to the ICM_GETBUFFERSWANTED message), we can render as soon as we
|
|
* get the draw request.
|
|
*/
|
|
DWORD
|
|
Draw(
|
|
PINSTINFO pinst,
|
|
ICDRAW * icinfo,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
DRAWBUFFER Draw;
|
|
LPBITMAPINFOHEADER lpbi;
|
|
|
|
/*
|
|
* do we have anything to do ? As we don't do inter-frame compression or
|
|
* any form of pre-buffering, we can do nothing for any of these
|
|
* occasions
|
|
*/
|
|
if (icinfo->dwFlags & (ICDRAW_HURRYUP | ICDRAW_PREROLL | ICDRAW_NULLFRAME)) {
|
|
return(ICERR_OK);
|
|
}
|
|
|
|
/*
|
|
* UPDATE means draw an existing frame, rather than a new frame.
|
|
* Sometimes we will not get data - in this case it is already in
|
|
* the hardware and we need do nothing (a separate draw-window message
|
|
* will have been sent to sync the overlay region).
|
|
*
|
|
* If, however, there is data, then we should draw it. Update in this
|
|
* case means that the data is not a delta on a previous frame. However,
|
|
* we may never have seen this frame before, so it may not be in the
|
|
* frame buffer.
|
|
*/
|
|
if ((icinfo->dwFlags & ICDRAW_UPDATE) &&
|
|
((icinfo->cbData == 0) || (icinfo->lpData == NULL))) {
|
|
return(ICERR_OK);
|
|
}
|
|
|
|
Draw.lpData = icinfo->lpData;
|
|
Draw.rcSource = pinst->rcSource;
|
|
Draw.Format = FOURCC_YUV411;
|
|
lpbi = (LPBITMAPINFOHEADER) icinfo->lpFormat;
|
|
Draw.ulWidth = lpbi->biWidth;
|
|
Draw.ulHeight = lpbi->biHeight;
|
|
|
|
/* check that a draw-begin has happened */
|
|
if (pinst->vh == NULL) {
|
|
return((DWORD) ICERR_ERROR);
|
|
}
|
|
|
|
if (!VC_DrawFrame(pinst->vh, &Draw)) {
|
|
return((DWORD) ICERR_ERROR);
|
|
}
|
|
|
|
return(ICERR_OK);
|
|
|
|
}
|
|
|
|
/*
|
|
* stop rendering, and disable overlay. In fact, this function is not
|
|
* called in response to the ICM_DRAW_END message as this comes too early -
|
|
* it is done in response to device close. see drvproc.c for draw message
|
|
* handling comments.
|
|
*/
|
|
DWORD
|
|
DrawEnd(PINSTINFO pinst)
|
|
{
|
|
|
|
if (pinst->vh) {
|
|
|
|
dprintf2(("close yuv hardware"));
|
|
|
|
VC_Overlay(pinst->vh, FALSE);
|
|
VC_CloseDevice(pinst->vh);
|
|
pinst->vh = NULL;
|
|
}
|
|
|
|
if (pinst->hKeyBrush) {
|
|
DeleteObject(pinst->hKeyBrush);
|
|
pinst->hKeyBrush = NULL;
|
|
}
|
|
|
|
return(ICERR_OK);
|
|
}
|
|
|
|
/*
|
|
* window has moved.
|
|
* we are given the new dest-rect in screen co-ords - but possibly only the
|
|
* vis-region or z-ordering have changed.
|
|
*/
|
|
DWORD
|
|
DrawWindow(PINSTINFO pinst, PRECT prc)
|
|
{
|
|
pinst->rcDest = *prc;
|
|
|
|
return(PlaceOverlay(pinst));
|
|
}
|
|
|
|
|
|
|