DxgiOuputDuplication texture frames to DirectX VideoProcessor - c++

I was playing with this project DirectXVideoScreen because I am trying to find a way to convert RGBA to NV12 so I can make a video using the h264 transformation of my GPU. (You can only use the transformation when you have NV12 or YUV2 color format)
I have tried doing the color conversion using the Windows Media Foundation MFT (Video processor) but I am trying to go directly and use DirectX in order to have more control.
While you duplicate the screen the texture you get back is of type DXGI_FORMAT_B8G8R8A8_UNORM and I have to transform it to DXGI_FORMAT_NV12.
So I took the code from the project "D3D11VideoProcessor" and added the code from the "SimpleDesktopDuplication" project.
It is like this.
CD3D11VideoProcessor cD3D11VideoProcessor;
CSimpleDesktopDuplication cSimpleDesktopDuplication;
ID3D11Texture2D* output = NULL;
if (cSimpleDesktopDuplication.InitDesktopDuplication() == S_OK)
{
output = cSimpleDesktopDuplication.ProcessDesktopDuplication();
}
if(cD3D11VideoProcessor.InitDXVA2(2560,1440, DXGI_FORMAT_NV12) == S_OK)
{
cD3D11VideoProcessor.ProcessImageConversion(output, CONVERTED_IMAGE);
}
The problem (I think?) is that
m_pDXGIOutputDuplication->AcquireNextFrame(500, &FrameInfo, &pDXGIResource));
pDXGIResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&pScreenShotTexture2D)));
Returns a texture with the following flags.
Width: a00
Height: 5a0
MipLevels: 1
ArraySize: 1
Format: 57
Usage: 0
BindFlags: 28 // D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE
CPUAccessFlags: 0
MiscFlags: 2900
And here the code fails while executing
m_pD3D11VideoContext->VideoProcessorBlt(m_pD3D11VideoProcessor, pD3D11VideoProcessorOutputView, 0, 1, &StreamData)
HRESULT CD3D11VideoProcessor::ProcessImageConversion(ID3D11Texture2D* outputDuplicationTexture, LPCWSTR wszOutputImageFile)
{
HRESULT hr = S_OK;
ID3D11VideoProcessorInputView* pD3D11VideoProcessorInputViewIn = NULL;
ID3D11VideoProcessorOutputView* pD3D11VideoProcessorOutputView = NULL;
ID3D11VideoDevice* pD3D11VideoDevice = NULL;
ID3D11Texture2D* pOutTexture2D = NULL;
IF_FAILED_RETURN(m_pD3D11VideoContext == NULL ? E_UNEXPECTED : S_OK);
try
{
IF_FAILED_THROW(m_pD3D11Device->QueryInterface(__uuidof(ID3D11VideoDevice), reinterpret_cast<void**>(&pD3D11VideoDevice)));
D3D11_TEXTURE2D_DESC desc2D;
desc2D.Width = 2560;
desc2D.Height = 1440;
desc2D.MipLevels = 1;
desc2D.ArraySize = 1;
desc2D.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc2D.SampleDesc.Count = 1;
desc2D.SampleDesc.Quality = 0;
desc2D.Usage = D3D11_USAGE_DEFAULT;
desc2D.BindFlags = D3D11_BIND_RENDER_TARGET;
desc2D.CPUAccessFlags = 0;
desc2D.MiscFlags = 0;
D3D11_TEXTURE2D_DESC desc2Dtmp;
outputDuplicationTexture->GetDesc(&desc2Dtmp);
printf("Width: %x\n", desc2Dtmp.Width);
printf("Height: %x\n", desc2Dtmp.Height);
printf("MipLevels: %x\n", desc2Dtmp.MipLevels);
printf("ArraySize: %x\n", desc2Dtmp.ArraySize);
printf("Format: %x\n", desc2Dtmp.Format);
printf("Usage: %x\n", desc2Dtmp.Usage);
printf("BindFlags: %x\n", desc2Dtmp.BindFlags);
printf("CPUAccessFlags: %x\n", desc2Dtmp.CPUAccessFlags);
printf("MiscFlags: %x\n", desc2Dtmp.MiscFlags);
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC pInDesc;
ZeroMemory(&pInDesc, sizeof(pInDesc));
pInDesc.FourCC = 0;
pInDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
pInDesc.Texture2D.MipSlice = 0;
pInDesc.Texture2D.ArraySlice = 0;
IF_FAILED_THROW(pD3D11VideoDevice->CreateVideoProcessorInputView(outputDuplicationTexture, m_pD3D11VideoProcessorEnumerator, &pInDesc, &pD3D11VideoProcessorInputViewIn));
desc2D.Format = DXGI_FORMAT_NV12;
IF_FAILED_THROW(m_pD3D11Device->CreateTexture2D(&desc2D, NULL, &pOutTexture2D));
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC pOutDesc;
ZeroMemory(&pOutDesc, sizeof(pOutDesc));
pOutDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
pOutDesc.Texture2D.MipSlice = 0;
IF_FAILED_THROW(pD3D11VideoDevice->CreateVideoProcessorOutputView(pOutTexture2D, m_pD3D11VideoProcessorEnumerator, &pOutDesc, &pD3D11VideoProcessorOutputView));
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
ZeroMemory(&StreamData, sizeof(StreamData));
StreamData.Enable = TRUE;
StreamData.OutputIndex = 0;
StreamData.InputFrameOrField = 0;
StreamData.PastFrames = 0;
StreamData.FutureFrames = 0;
StreamData.ppPastSurfaces = NULL;
StreamData.ppFutureSurfaces = NULL;
StreamData.pInputSurface = pD3D11VideoProcessorInputViewIn;
StreamData.ppPastSurfacesRight = NULL;
StreamData.ppFutureSurfacesRight = NULL;
/*Error is here !!!*/
IF_FAILED_THROW(m_pD3D11VideoContext->VideoProcessorBlt(m_pD3D11VideoProcessor, pD3D11VideoProcessorOutputView, 0, 1, &StreamData));
IF_FAILED_THROW(CreateBmpFileFromYUVSurface(pOutTexture2D, wszOutputImageFile));
}
catch(HRESULT){}
SAFE_RELEASE(pOutTexture2D);
SAFE_RELEASE(outputDuplicationTexture);
SAFE_RELEASE(pD3D11VideoProcessorOutputView);
SAFE_RELEASE(pD3D11VideoProcessorInputViewIn);
SAFE_RELEASE(pD3D11VideoDevice);
return hr;
}
The error code is 0x80070057 meaning E_INVALIDARG One or more arguments are invalid.. I can not find the invalid arg :/
Can you help me ? or guide me ? Maybe I have to somehow change the ID3D11Texture2D* outputDuplicationTexture to match a simpler version of the texture (change misc params) ?
Thank you!

Related

How to properly use SetDisplayConfig with multiple monitors?

I`m creating a small programm that will include all displays in desktop(extended mode) or disable all secondary displays (displays can be connected to gpus and integrated graphics).
This programm is for Windows 7, so relying on information from internet i decided to use CCD APIs, but encounted a problem with SetDisplayConfig() function.
For example this code to turn off all secondary displays works perfectly, as 'i' increments one of displays turns off:
UINT32 PathCount = 0; //path count
UINT32 ModeCount = 0; //mode count
HRESULT hr;
hr = GetDisplayConfigBufferSizes(QDC_ALL_PATHS, &PathCount, &ModeCount);
std::vector<DISPLAYCONFIG_PATH_INFO> pathArray(PathCount);
std::vector<DISPLAYCONFIG_MODE_INFO> modeArray(ModeCount);
hr = QueryDisplayConfig(QDC_ALL_PATHS, &PathCount, &pathArray[0], &ModeCount, &modeArray[0], NULL);
for (int i = 1; i < PathCount;i++)
{
if(pathArray[i].flags != 0)
{
pathArray[i].flags = 0;
hr = SetDisplayConfig(PathCount, &pathArray[0], ModeCount, &modeArray[0], SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_ALLOW_CHANGES);
}
}
To extend a display i found this code:
SetDisplayConfig(0, NULL, 0, NULL, SDC_TOPOLOGY_EXTEND | SDC_APPLY);
This function with this specific parameters works, but it targeting only my second display which is conected to gpu as my primary display, but not the third display which is conected to motherboard ( only after i phisicly disconect my second display from gpu, this function works with display conected to motherboard).
I tried to use
for (int i = 1; i < PathCount;i++)
{
if(pathArray[i].flags != 1)
{
pathArray[i].flags = 1;
hr = SetDisplayConfig(PathCount, &pathArray[0], ModeCount, &modeArray[0], SDC_TOPOLOGY_EXTEND | SDC_APPLY | SDC_PATH_PERSIST_IF_REQUIRED);
}
}
but receiving ERROR_ADAP_HDW_ERR error
So i`m asking to help me. How to target specific display(or all displays at once) using SetDisplayConfig() finction with 'SDC_TOPOLOGY_EXTEND' flag, or there is another approach to resolve this problem ?
So i made it, it doesn't work with some exotic display setup(like multiple usb displays), but it works, and all displays are added to desktop(or desktop is streched to all displays)
How i done it ?
Microsoft detours helped me, by monitoring what system settings in windows 10 do when i add display to desktop, it just sets most important properties to default values.
commented parts works only on win 10.
HRESULT hr = S_OK;
UINT32 NumPathArrayElements = 0;
UINT32 NumModeInfoArrayElements = 0;
//LONG error = GetDisplayConfigBufferSizes((QDC_ALL_PATHS | QDC_VIRTUAL_MODE_AWARE), &NumPathArrayElements, &NumModeInfoArrayElements);
hr = GetDisplayConfigBufferSizes((QDC_ALL_PATHS), &NumPathArrayElements, &NumModeInfoArrayElements);
std::vector<DISPLAYCONFIG_PATH_INFO> PathInfoArray2(NumPathArrayElements);
std::vector<DISPLAYCONFIG_MODE_INFO> ModeInfoArray2(NumModeInfoArrayElements);
//error = QueryDisplayConfig((QDC_ALL_PATHS | QDC_VIRTUAL_MODE_AWARE), &NumPathArrayElements, &PathInfoArray2[0], &NumModeInfoArrayElements, &ModeInfoArray2[0], NULL);
hr = QueryDisplayConfig((QDC_ALL_PATHS), &NumPathArrayElements, &PathInfoArray2[0], &NumModeInfoArrayElements, &ModeInfoArray2[0], NULL);
struct displaySourcePair
{
std::wstring displayName;
UINT32 displayId;
};
std::vector<displaySourcePair> ocupiedDisplays;
if (hr == S_OK)
{
DISPLAYCONFIG_SOURCE_DEVICE_NAME SourceName = {};
SourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
SourceName.header.size = sizeof(SourceName);
DISPLAYCONFIG_TARGET_PREFERRED_MODE PreferedMode = {};
PreferedMode.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE;
PreferedMode.header.size = sizeof(PreferedMode);
int newId = 0;
for (UINT32 i = 0; i < NumPathArrayElements; i++)
{
bool match = false;
SourceName.header.adapterId = PathInfoArray2[i].sourceInfo.adapterId;
SourceName.header.id = PathInfoArray2[i].sourceInfo.id;
PreferedMode.header.adapterId = PathInfoArray2[i].targetInfo.adapterId;
PreferedMode.header.id = PathInfoArray2[i].targetInfo.id;
hr = HRESULT_FROM_WIN32(DisplayConfigGetDeviceInfo(&SourceName.header));
hr = HRESULT_FROM_WIN32(DisplayConfigGetDeviceInfo(&PreferedMode.header));
if (hr == S_OK)
{
if ((PathInfoArray2[i].flags & DISPLAYCONFIG_PATH_ACTIVE) == true)
{
std::wstring str = std::wstring(SourceName.viewGdiDeviceName);
displaySourcePair tmpStruct;
tmpStruct.displayId = PreferedMode.header.id;
tmpStruct.displayName = str;
ocupiedDisplays.push_back(tmpStruct);
}
for (int k = 0; k < ocupiedDisplays.size(); k++)
{
std::wstring str = std::wstring(SourceName.viewGdiDeviceName);
if (ocupiedDisplays[k].displayName == str || ocupiedDisplays[k].displayId == PreferedMode.header.id)
{
match = true;
}
}
if (match == false && PathInfoArray2[i].targetInfo.targetAvailable == 1)
{
PathInfoArray2[i].flags |= DISPLAYCONFIG_PATH_ACTIVE;
std::wstring str = std::wstring(SourceName.viewGdiDeviceName);
displaySourcePair tmpStruct;
tmpStruct.displayId = PreferedMode.header.id;
tmpStruct.displayName = str;
ocupiedDisplays.push_back(tmpStruct);
}
if (PathInfoArray2[i].targetInfo.targetAvailable == 1)
{
PathInfoArray2[i].sourceInfo.id = newId;
newId++;
}
if (PathInfoArray2[i].targetInfo.id != PreferedMode.header.id)
{
PathInfoArray2[i].targetInfo.id = PreferedMode.header.id;
}
PathInfoArray2[i].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
PathInfoArray2[i].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
}
}
//hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_VALIDATE | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES | SDC_VIRTUAL_MODE_AWARE));
//hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_APPLY | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES | SDC_VIRTUAL_MODE_AWARE));
hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_VALIDATE | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES));
hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_APPLY | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES));
}

Extract dirty rects RGB pixel buffer data DirectX

I'm using Desktop Duplication from Windows API.
Here is the code to access next frame and get the rectangle of pixels that have change from previous frame.
//
// Get next frame and write it into Data
//
_Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS)
DUPL_RETURN DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data, _Out_ bool* Timeout)
{
IDXGIResource* DesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(10000, &FrameInfo, &DesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return DUPL_RETURN_SUCCESS;
}
*Timeout = false;
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
}
// If still holding old frame, destroy it
if (m_AcquiredDesktopImage)
{
m_AcquiredDesktopImage->Release();
m_AcquiredDesktopImage = nullptr;
}
// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = nullptr;
if (FAILED(hr))
{
return ProcessFailure(nullptr, L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr);
}
// Get metadata
if (FrameInfo.TotalMetadataBufferSize)
{
// Old buffer too small
if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize)
{
if (m_MetaDataBuffer)
{
delete [] m_MetaDataBuffer;
m_MetaDataBuffer = nullptr;
}
m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
if (!m_MetaDataBuffer)
{
m_MetaDataSize = 0;
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
}
m_MetaDataSize = FrameInfo.TotalMetadataBufferSize;
}
UINT BufSize = FrameInfo.TotalMetadataBufferSize;
// Get move rectangles
hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize);
if (FAILED(hr))
{
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
}
Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
BYTE* DirtyRects = m_MetaDataBuffer + BufSize;
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
// Get dirty rectangles
hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
if (FAILED(hr))
{
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors);
}
Data->DirtyCount = BufSize / sizeof(RECT);
Data->MetaData = m_MetaDataBuffer;
}
Data->Frame = m_AcquiredDesktopImage;
Data->FrameInfo = FrameInfo;
//Here I would like to access pixel data from Data->Frame. A buffer of RGBA pixel
return DUPL_RETURN_SUCCESS;
}
Here is Frame_Data structure
typedef struct _FRAME_DATA
{
ID3D11Texture2D* Frame;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
_Field_size_bytes_((MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT)) + (DirtyCount * sizeof(RECT))) BYTE* MetaData;
UINT DirtyCount;
UINT MoveCount;
} FRAME_DATA;
Is it possible to access pixel buffer data that have been modified from Data->Frame
Here is my code to access data :
BYTE* DISPLAYMANAGER::GetImageData(ID3D11Texture2D* texture2D, D3D11_TEXTURE2D_DESC Desc)
{
if (texture2D != NULL)
{
D3D11_TEXTURE2D_DESC description;
texture2D->GetDesc(&description);
description.BindFlags = 0;
description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
description.Usage = D3D11_USAGE_STAGING;
description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
ID3D11Texture2D* texTemp = NULL;
HRESULT hr = m_Device->CreateTexture2D(&description, NULL, &texTemp);
if (FAILED(hr))
{
if (texTemp)
{
texTemp->Release();
texTemp = NULL;
}
return NULL;
}
m_DeviceContext->CopyResource(texTemp, texture2D);
D3D11_MAPPED_SUBRESOURCE mapped;
unsigned int subresource = D3D11CalcSubresource(0, 0, 0);
hr = m_DeviceContext->Map(texTemp, subresource, D3D11_MAP_READ_WRITE, 0, &mapped);
if (FAILED(hr))
{
texTemp->Release();
texTemp = NULL;
return NULL;
}
unsigned char *captureData = new unsigned char[Desc.Width * Desc.Height * 4];
RtlZeroMemory(captureData, Desc.Width * Desc.Height * 4);
const int pitch = mapped.RowPitch;
unsigned char *source = static_cast<unsigned char*>(mapped.pData);
unsigned char *dest = captureData;
for (int i = 0; i < Desc.Height; i++) {
memcpy(captureData, source, Desc.Width * 4);
source += pitch;
captureData += Desc.Width * 4;
}
for (int i = 0; i < Desc.Width * Desc.Height * 4; i++) {
//trace(L"Pixel[%d] = %x\n", i, dest[i]);
}
m_DeviceContext->Unmap(texTemp, 0);
return dest;
}
else
return NULL;
}
Thank you for your help!
The textures you obtain via duplication API are not necessarily accessible for CPU, for individual pixel access. To read the texture data, you might need to create a mappable staging texture and copy the obtained texture there. Then doing the mapping you would get a pointer to actual data. Note that this is, in general, not a performance friendly operation.
You will find related information in other answers as well:
How to work with pixels using Direct2D:
For those times when you absolutely have to do CPU pixel manipulation but still want a substantial degree of acceleration, you can manage your own mappable D3D11 textures. For example, you can use staging textures if you want to asynchronously manipulate your texture resources from the CPU.
Transferring textures across adapters in DirectX 11:
... copies it to a staging resource (created on the same device) using ID3D11DeviceContext::CopyResource. I then map that staging resource with Read...

Split Contiguous Buffer in RGB Channels (IMFMediaBuffer)

I'm trying to split up an contiguous buffer into 3 byte channels (RGB). Here is my acutal workflow to get the buffer filled with an image:
Set up an Source Reader (MFVideoFormat_RGB32)
Receive video format information
Read first image and convert to contiguous buffer...
In addition to that, here is the code:
HRESULT hr = S_OK;
IMFAttributes *attributes = NULL;
SafeRelease(&_sourcereader);
hr = MFCreateAttributes(&attributes, 1);
if (FAILED(hr)) {
// TODO: set error
return false;
}
hr = attributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, true);
if (FAILED(hr)) {
// TODO: set error
return false;
}
// conversion from qstring to const wchar*
const WCHAR* wfilename = filename.toStdWString().c_str();
// create source reader from file with attributes
hr = MFCreateSourceReaderFromURL(wfilename, attributes, &_sourcereader);
if (FAILED(hr)) {
// TODO: set error
return false;
}
// configure sourcereader for progressive RGB32 frames
IMFMediaType *mediatype = NULL;
hr = MFCreateMediaType(&mediatype);
if (SUCCEEDED(hr))
{
hr = mediatype->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if (SUCCEEDED(hr))
{
hr = mediatype->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
}
if (SUCCEEDED(hr))
{
hr = _sourcereader->SetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
NULL, mediatype);
}
// Ensure the stream is selected.
if (SUCCEEDED(hr))
{
hr = _sourcereader->SetStreamSelection(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE);
}
if (FAILED(hr)) {
// TODO: Error log for failed configuration
std::cout << "(ConfigureSourceReader) Configuration failed" << std::endl;
return false;
}
//------------------------------------------------------------------
//---------------------- Get Video Format Infos --------------------
//------------------------------------------------------------------
GUID subtype = { 0 };
// Get the media type from the stream.
hr = _sourcereader->GetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &mediatype );
// Make sure it is a video format.
hr = mediatype->GetGUID(MF_MT_SUBTYPE, &subtype);
if (subtype != MFVideoFormat_RGB32)
{
hr = E_UNEXPECTED;
// TODO: Error log message
SafeRelease(&mediatype);
return false;
}
//------------------------------------------------------------------
// Get the width and height
UINT32 width = 0, height = 0;
hr = MFGetAttributeSize(mediatype, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
{
// TODO: Error log message
SafeRelease(&mediatype);
return false;
}
//assign dimensions to VideoInfo
_videoinfo.imageHeight = height; _videoinfo.imageWidth = width;
//std::cout << "(GetVideoFormat) width: " << width << ", height: " << height << std::endl;
//------------------------------------------------------------------
//get framerate
UINT32 framerate_num = 0, framerate_denom = 0;
hr = MFGetAttributeRatio(mediatype, MF_MT_FRAME_RATE, &framerate_num, &framerate_denom);
if (FAILED(hr))
{
// TODO: Error log message
SafeRelease(&mediatype);
return false;
}
//set frame rate in struct
_videoinfo.fps = framerate_num / framerate_denom; // TODO: check for valid fps 24,25,30 ...
//------------------------------------------------------------------
// Get length
LONGLONG length = 0;
PROPVARIANT var;
PropVariantInit(&var);
hr = _sourcereader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE,
MF_PD_DURATION,
&var
);
if (SUCCEEDED(hr)) {
assert(var.vt == VT_UI8);
length = var.hVal.QuadPart;
} else {
// TODO : erro log msg
return false;
}
//Get total framenumber and length: save to info struct
_videoinfo.noofFrames = length / 10000000 * this->getFrameRate(); // incl. conversion from nano sec to sec
_videoinfo.duration = length;
//------------------------------------------------------------------
// Get the stride to find out if the bitmap is top-down or bottom-up.
LONG lStride = 0;
lStride = (LONG)MFGetAttributeUINT32(mediatype, MF_MT_DEFAULT_STRIDE, 1);
_videoinfo.stride = lStride;
_videoinfo.bTopDown = (lStride > 0);
//------------------------------------------------------------------
SafeRelease(&mediatype);
// return true and flag if initialization went well
_bInitialized = true;
return true;
After that I call a function to read a single frame (at the moment the first one).
HRESULT hr = S_OK;
IMFSample *pSample = NULL;
IMFMediaBuffer *buffer = NULL;
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
// Read Sample (RGB32)
hr = _sourcereader->ReadSample (
(DWORD) MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
&streamIndex,
&flags,
&llTimeStamp,
&pSample);
if (FAILED (hr)) {
// TODO handle fail case
}
//convert sample data to buffer
hr = pSample->ConvertToContiguousBuffer(&buffer);
if (FAILED (hr)) {
// TODO handle fail case
}
I know that by calling the function buffer->Lock(&pixels, NULL, &nPixels) that I can get the BYTE-stream stored in pixels. In my case I create a custom image with the given height and width (from SourceReader; [first function]). From the empty image I can get an empty color matrix which has to be filled with the following funtion: Color (byte red, byte green, byte blue)
I dont know how to split my RGB32 BYTE array into the single channels to fill my image? Maybe it is a silly question but I am relatively new to this area...
For RGB32 the byte format is:
R=Red
G=Green
B=Blue
A=Transparency
RGBARGBARGBA...
A very simple pseudo-code example of extracting the channels is shown below.
for (int row = 0; row < height; row++) {
for (int col = 0; col < stride; col += 4) {
redBuf[rIndex++] = sample[row * stride + col];
greenBuf[gIndex++] = sample[row * stride + col + 1];
blueBuf[bIndex++] = sample[row * stride + col + 2];
transparencyBuf[tIndex++] = sample[row * stride + col + 3];
}
}

How do I get the font color from a piece of HTML source code?

I have a piece of HTML source like this:
<FONT color=#5a6571>Beverly Mitchell</FONT> <FONT color=#5a6571>Shawnee Smith</FONT> <FONT color=#5a6571>Glenn Plummer</FONT> <NOBR>more >></NOBR>
I tried to retrieve the "color" value, like this:
MSHTML::IHTMLDocument2Ptr htmDoc1 = NULL;
SAFEARRAY *psaStrings1 = SafeArrayCreateVector(VT_VARIANT, 0, 1);
CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**) &htmDoc1);
VARIANT *param1 = NULL;
HRESULT hr = SafeArrayAccessData(psaStrings1, (LPVOID*)&param1);
param1->vt = VT_BSTR;
param1->bstrVal = SysAllocString(varSrc1.bstrVal);
hr = SafeArrayUnaccessData(psaStrings1);
hr = htmDoc1->write(psaStrings1);
MSHTML::IHTMLElementPtr pElemBody1 = NULL;
MSHTML::IHTMLDOMNodePtr pHTMLBodyDOMNode1 =NULL;
hr = htmDoc1->get_body(&pElemBody1);
if(SUCCEEDED(hr))
{
hr = pElemBody1->QueryInterface(IID_IHTMLDOMNode,(void**)&pHTMLBodyDOMNode1);
if(SUCCEEDED(hr))
{
ProcessDomNodeSmartWrapper(pHTMLBodyDOMNode1, ProcTgtTagStrVec);
}
}
long lLength = 0;
MSHTML::IHTMLElementCollectionPtr pElemColl1 = NULL;
MSHTML::IHTMLElementPtr pChElem1 = NULL;
MSHTML::IHTMLStylePtr pStyle1 = NULL;
IDispatchPtr ppvdisp1 = NULL;
hr = htmDoc1->get_all(&pElemColl1);
hr = pElemColl1->get_length(&lLength);
for(long i = 0; i < lLength; i++)
{
_variant_t name(i);
_variant_t index(i);
ppvdisp1 = pElemColl1->item(name, index);
if(ppvdisp1 && SUCCEEDED(hr))
{
hr = ppvdisp1->QueryInterface(IID_IHTMLElement, (void **)&pChElem1);
if(pChElem1 && SUCCEEDED(hr))
{
BSTR bstrTagName = NULL;
pChElem1->get_tagName(&bstrTagName);
hr = pChElem1->get_style(&pStyle1);
if(pStyle1 && SUCCEEDED(hr))
{
_variant_t varFtCol;
hr = pStyle1->get_color(&varFtCol);
if(hr = S_OK && varFtCol)
{
hmStyles1[wstring(varFtCol.bstrVal)] = L"FontColor";
}
}
if(bstrTagName)
SysFreeString(bstrTagName);
} // if pStyle && SUCCEEDED(hr)
}//if ppvdisp && SUCCEEDED(hr)
}//for
But I can never get the "color" value - varFtCol.bstrVal is a bad pointer when I debug the program. This is what varFtCol showed when I debug the program:
- varFtCol {???} _variant_t
- tagVARIANT BSTR = 0x00000000 tagVARIANT
vt 8 unsigned short
- BSTR 0x00000000 wchar_t *
CXX0030: Error: expression cannot be evaluated
#5a6571 is a hex color represents for RGB value of (90,101,113).
How can I get this color info?
You shouldn't be getting style on pChElem1 because the color is not part of style in your case. Color is part of Font element.
Instead you must call pChElem1->getAttribute("color" . . .)
This will return #5a6571
The following code is in MFC. But you can easily convert to regular Win32 if you are not using MFC.
COLORREF GetColorFromHexString( CString szColor )
{
TCHAR *szScan;
CString strTemp;
CString strColor = szColor;
long lRR = 0,lGG = 0,lBB = 0;
//first we will remove # characters which come from XML document
strColor.TrimLeft(_T('#'));
strColor.TrimRight(_T('#'));
//it should be of the form RRGGBB
if (strColor.GetLength() == 6) {
//get red color, from the hexadecimal string
strTemp = strColor.Left(2);
lRR = _tcstol(LPCTSTR(strTemp),&szScan,16);
//get green color
strTemp = strColor.Mid(2,2);
lGG = _tcstol(LPCTSTR(strTemp),&szScan,16);
//get blue color
strTemp = strColor.Right(2);
lBB = _tcstol(LPCTSTR(strTemp),&szScan,16);
}
return RGB(lRR,lGG,lBB);
}
According to the MSDN documentation, IHTMLStyle::get_color may return either a BSTR or an integer value in the variant. Have you tried assigning varFtCol into an integer value and examining that result?
const int colorValue = static_cast<int>(varFtCol);
As a recommendation, when working with _variant_t, it is usually best to use the built-in casting operators than to direct access the members of the union itself.

Creating VHD files with the VirtualDisk API

I'm trying to use a VSS snapshot as the source for CreateVirtualDisk(). Environment/tools are C++ VS2008SP1 and 7.1 SDK on W7x64Ultimate
[Edited]
This works on Windows 7 x64
BOOL CreateVHD_Fixed(PCWSTR pszVhdPath, ULONG sizeInMB)
{
BOOL bRet = FALSE;
HANDLE hvhd;
CREATE_VIRTUAL_DISK_PARAMETERS params;
VIRTUAL_DISK_ACCESS_MASK mask;
VIRTUAL_STORAGE_TYPE vst =
{
VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
};
wprintf(L"CreateVHD_Fixed %s, size (MB) %d\n", pszVhdPath, sizeInMB);
params.Version1.UniqueId = GUID_NULL;
params.Version1.BlockSizeInBytes = 0;
params.Version1.MaximumSize = sizeInMB * 1024 * 1024;
params.Version1.ParentPath = NULL;
params.Version1.SourcePath = NULL;
params.Version1.SectorSizeInBytes = 512;
params.Version = CREATE_VIRTUAL_DISK_VERSION_1;
mask = VIRTUAL_DISK_ACCESS_CREATE;
DWORD ret = CreateVirtualDisk(&vst,
pszVhdPath,
mask,
NULL,
// To create a dynamic disk, use CREATE_VIRTUAL_DISK_FLAG_NONE instead.
CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION,
0,
&params,
NULL,
&hvhd);
if (ret == ERROR_SUCCESS)
{
bRet = TRUE;
}
else
{
bRet = FALSE;
printf("failed to create vdisk...err 0x%x\n", ret);
PrintErrorMessage(GetLastError());
}
if (INVALID_HANDLE_VALUE != hvhd)
{
CloseHandle(hvhd);
}
return bRet;
}
[Edited] - now failing in a different way with ERROR_INVALID_PARAMETER. Parameters are below with a root path of "\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy64"
VIRTUAL_STORAGE_TYPE storageType =
{
VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
// do not use any other GUID else you get an unknown provider error
VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT // **critical!**
};
VIRTUAL_DISK_ACCESS_MASK vdam = (VIRTUAL_DISK_ACCESS_MASK)(VIRTUAL_DISK_ACCESS_CREATE); // |VIRTUAL_DISK_ACCESS_WRITABLE|VIRTUAL_DISK_ACCESS_READ|VIRTUAL_DISK_ACCESS_GET_INFO);
CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION; // CREATE_VIRTUAL_DISK_FLAG_NONE;
CREATE_VIRTUAL_DISK_PARAMETERS parameters;
//
parameters.Version = CREATE_VIRTUAL_DISK_VERSION_1;
parameters.Version1.UniqueId = GUID_NULL;
parameters.Version1.MaximumSize = 0;
parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE;
parameters.Version1.ParentPath = 0;
parameters.Version1.SourcePath = root.c_str();
parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE;
ULONG ProviderSpecificFlags = 0;
HANDLE handle = 0;
dwRet = CreateVirtualDisk(&storageType,
_T("t:\\test.vhd"),
vdam,
NULL,
flags,
ProviderSpecificFlags,
&parameters,0,&handle);
Any ideas? The virtual disk API does not seem to have much example code.
Thx++
Jerry.
Jerry,
Use CreateVirtualDisk API to create a VHD or a differential VHD, make sure u send the right parameters. You have to specify the parent hard disk but not the source hard disk. Care must also be taken while using the flags.
Refer the links below:
"http://code.msdn.microsoft.com/windowshardware/CppVhdAPI-4412d182"
and
"http://code.msdn.microsoft.com/windowsdesktop/Virtual-hard-disk-03108ed3"