Get Raw Pixel Data with DirectXTK - c++

I would like to access pixel buffer data from Dirty Rect after calling AcquireNextFrame from DXGI.
Here is my code to get pixel buffer data from D3D11Texture2D :
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;
}
Output of dest is full of '0' value. Even mapped.pData is full of '0'. I don't know why.
Here is where I call my function GetImageData
DUPL_RETURN DISPLAYMANAGER::ProcessFrame(_In_ FRAME_DATA* Data, _Inout_ ID3D11Texture2D* SharedSurf, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc)
{
DUPL_RETURN Ret = DUPL_RETURN_SUCCESS;
// Process dirties and moves
if (Data->FrameInfo.TotalMetadataBufferSize)
{
D3D11_TEXTURE2D_DESC Desc;
Data->Frame->GetDesc(&Desc);
if (Data->MoveCount)
{
Ret = CopyMove(SharedSurf, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(Data->MetaData), Data->MoveCount, OffsetX, OffsetY, DeskDesc, Desc.Width, Desc.Height);
if (Ret != DUPL_RETURN_SUCCESS)
{
return Ret;
}
}
if (Data->DirtyCount)
{
Ret = CopyDirty(Data->Frame, SharedSurf, reinterpret_cast<RECT*>(Data->MetaData + (Data->MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT))), Data->DirtyCount, OffsetX, OffsetY, DeskDesc, Desc);
GetImageData(Data->Frame, Desc); //here I would like to extract the dirty rect buffer pixel (BGRA value).
}
}
return Ret;
}
Is it possible to do it ? I got this code from Microsoft Sample Desktop Duplication API. Thanks!

Related

The byte stream type of the given URL is unsupported MediaFoundation

I need to create a reader from buffer(that is wav bytes) in order to do it I am using such approach:
void co_AudioEncoderMF::load_from_memory(unsigned char const * data, long data_size)
{
unsigned char * buf = new unsigned char[data_size]; //JUST FOR TEST IN ORDER TO BE SURE THAT I HAVE THIS DATA
memcpy(buf, data, data_size);
IMFMediaType *input_type = nullptr;
IMFSourceReader *source_reader = nullptr;
IMFMediaType *ouput_media_type = nullptr;
IMFSinkWriter *sink_writer = nullptr;
IStream * stream = nullptr;
IMFByteStream * byte_stream = nullptr;
HRESULT hr = InitAndStartCOMLib();
if (SUCCEEDED(hr))
{
hr = create_stream(buf, data_size, &stream); <---- HERE I CREATE A STREAM
}
if (SUCCEEDED(hr))
{
hr = MFCreateMFByteStreamOnStream(stream, &byte_stream);
}
HGLOBAL hGlobal = nullptr;
//IMFSourceReader *pReader = nullptr;
if (SUCCEEDED(hr))
{
GetHGlobalFromStream(stream, &hGlobal);
hr = create_source_reader(byte_stream, &source_reader); <----- HERE I CREATE A READER
}
...
}
HRESULT co_AudioEncoderMF::InitAndStartCOMLib()
{
HRESULT hr = S_OK;
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
// Initialize the COM library.
hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// Initialize the Media Foundation platform.
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION);
}
return hr;
}
HRESULT co_AudioEncoderMF::create_stream(unsigned char const * pData, long dataLen, IStream ** out_stream)
{
HGLOBAL m_phMem = ::GlobalAlloc(GMEM_MOVEABLE, dataLen);
if (!m_phMem)
{
printf("Error AfxThrowMemoryException");
}
LPVOID dest = ::GlobalLock(m_phMem);
memcpy(dest, pData, dataLen);
::GlobalUnlock(m_phMem);
return CreateStreamOnHGlobal(m_phMem, FALSE /*fDeleteOnRelease*/, out_stream);// NOTE :: in order to get more flexablity need to consider such an approach https://gist.github.com/alekseytimoshchenko/e8f52604fdeb50c8ad7873aeb8281bfa
}
HRESULT co_AudioEncoderMF::create_source_reader(IMFByteStream * in_byte_stream, IMFSourceReader **source_reader)
{
HRESULT hr = S_OK;
IMFAttributes * attr = nullptr;
hr = MFCreateAttributes(&attr, 10);
if (SUCCEEDED(hr))
{
hr = attr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true);
}
else
{
printf("Error MFCreateAttributes");
}
if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromByteStream(in_byte_stream, attr, source_reader); <----- AND HERE I GOT MY ERROR ABOUT UNSUPPORTED FORMAT
}
else
{
printf("Error Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)");
}
if (FAILED(hr))
{
printf("Error MFCreateSourceReaderFromByteStream");
}
return hr;
}
I am using the same approach but with mp3 bytes and it is works, I assume that it has a difference, but I don't know how to approve it and solve.
In my case problem was in that I tried to call this method MFCreateSourceReaderFromByteStream on stream that was created on wav bytes, but it didn't includes .wav header. So, I was needed to add a header. I used this method
/*static*/ void Utils::append_data_to_wav_header(unsigned char * buff, int buff_size, int num_of_channels, int sample_rate, std::vector<unsigned char> & out_buf)
{
//*** START OF HEADER
long idx = 0;
uint32_t uint_32_buf = buff_size + 44 /*header size*/;
out_buf.resize(uint_32_buf);
std::string str_buf = "RIFF";
memcpy(&out_buf[idx], str_buf.c_str(), strlen(str_buf.c_str()));
idx += (long)strlen(str_buf.c_str());
memcpy(&out_buf[idx], &uint_32_buf, sizeof(uint_32_buf));
idx += sizeof(uint_32_buf);
str_buf = "WAVE";
memcpy(&out_buf[idx], str_buf.c_str(), strlen(str_buf.c_str()));
idx += (long)strlen(str_buf.c_str());
str_buf = "fmt ";
memcpy(&out_buf[idx], str_buf.c_str(), strlen(str_buf.c_str()));
idx += (long)strlen(str_buf.c_str());
uint_32_buf = 16;
memcpy(&out_buf[idx], &uint_32_buf, 1);
idx += sizeof(uint_32_buf);
uint16_t uint_16_buff = 1;
memcpy(&out_buf[idx], &uint_16_buff, 1);
idx += sizeof(uint_16_buff);
uint_16_buff = num_of_channels;
memcpy(&out_buf[idx], &uint_16_buff, 1);
idx += sizeof(uint_16_buff);
uint_32_buf = sample_rate;
memcpy(&out_buf[idx], &uint_32_buf, 1);
idx += sizeof(uint_32_buf);
uint_32_buf = (sample_rate * 16 * num_of_channels) / 8;
memcpy(&out_buf[idx], &uint_32_buf, 1);
idx += sizeof(uint_32_buf);
uint_16_buff = 16 * num_of_channels / 8;
memcpy(&out_buf[idx], &uint_16_buff, 1);
idx += sizeof(uint_16_buff);
uint_16_buff = 16;
memcpy(&out_buf[idx], &uint_16_buff, 1);
idx += sizeof(uint_16_buff);
str_buf = "data";
memcpy(&out_buf[idx], str_buf.c_str(), strlen(str_buf.c_str()));
idx += (long)strlen(str_buf.c_str());
uint_32_buf = buff_size;
memcpy(&out_buf[idx], &uint_32_buf, 1);
idx += sizeof(uint_32_buf);
//*** END OF HEADER
memcpy(&out_buf[idx], buff, buff_size);
idx += buff_size;
}

E_INVALIDARG in MAP (D3D11)

Good afternoon.
I am trying to write a program that will display pixels for me from the desktop, but I get an E_INVALIDARG error in the Map function.
Below I have attached my function and indicated with a comment where I got this error. I also inserted the D3D11_CREATE_DEVICE_DEBUG flag in the D3D11CreateDevice function, but it doesn't output anything. Please help.
BYTE* GetImageData()
{
if (pDupTex2D != NULL)
{
printf("Tap1\n");
/*
D3D11_TEXTURE2D_DESC description;
pDupTex2D->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;*/
D3D11_TEXTURE2D_DESC Desc;
//Desc.Width = 3440;
//Desc.Height = 1440;
Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
Desc.Usage = D3D11_USAGE_STAGING;
Desc.BindFlags = 0;
Desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
pDupTex2D->GetDesc(&Desc);
ID3D11Texture2D *texTemp = NULL;
HRESULT hr = pD3DDev->CreateTexture2D(&Desc, NULL, &texTemp);
std::cout << std::hex<<hr << std::endl;
if (FAILED(hr))
{
if (texTemp)
{
texTemp->Release();
texTemp = NULL;
}
return NULL;
}
printf("Tap2\n");
pCtx->CopyResource(texTemp, pDupTex2D);
D3D11_MAPPED_SUBRESOURCE mapped;
unsigned int subresource = D3D11CalcSubresource(0, 0, 0);
hr = pCtx->Map(texTemp, subresource, D3D11_MAP_READ_WRITE, 0, &mapped); // ERROR ===================================
std::cout << std::hex << hr << std::endl;
if (FAILED(hr))
{
texTemp->Release();
texTemp = NULL;
return NULL;
}
printf("Tap3\n");
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++) {
printf("h");
std::cout << "Pixel[%d] = %x\n" << i << dest[i] << std::endl;
}
pCtx->Unmap(texTemp, 0);
return dest;
}
else {
return NULL;
}
}

Get Pixels Buffer from ID3D11Texture2D C++

I would like to extract pixels from ID3D11Texture2D.
I have this function :
DUPL_RETURN DISPLAYMANAGER::CopyDirty(_In_ ID3D11Texture2D* SrcSurface, _Inout_ ID3D11Texture2D* SharedSurf, _In_reads_(DirtyCount) RECT* DirtyBuffer, UINT DirtyCount, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc)
from desktop duplication sample https://code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a/sourcecode?fileId=42782&pathId=1384140008
Here is my code, the buffer returned is full of '\0'...
BYTE* DISPLAYMANAGER::GetImageData(ID3D11Device* device, ID3D11DeviceContext* context, 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;
ID3D11Texture2D* texTemp = NULL;
HRESULT hr = device->CreateTexture2D(&description, NULL, &texTemp);
if (FAILED(hr))
{
if (texTemp)
{
texTemp->Release();
texTemp = NULL;
}
return NULL;
}
context->CopyResource(texTemp, texture2D);
D3D11_MAPPED_SUBRESOURCE mapped;
unsigned int subresource = 0;
hr = context->Map(texTemp, 0, D3D11_MAP_READ, 0, &mapped);
if (FAILED(hr))
{
texTemp->Release();
texTemp = NULL;
return NULL;
}
Desc.Width = description.Width;
Desc.Height = description.Height;
const int pitch = mapped.RowPitch;
BYTE* source = (BYTE*)(mapped.pData);
BYTE* dest = new BYTE[(Desc.Width)*(Desc.Height) * 4];
BYTE* destTemp = dest;
for (int i = 0; i < Desc.Height; ++i)
{
memcpy(destTemp, source, Desc.Width * 4);
source += pitch;
destTemp += Desc.Width * 4;
}
context->Unmap(texTemp, 0);
return dest;
}
else
return NULL;
}
I pass this function in CopyDirty(_In_ ID3D11Texture2D* SrcSurface, _Inout_ ID3D11Texture2D* SharedSurf, _In_reads_(DirtyCount) RECT* DirtyBuffer,
UINT DirtyCount, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc, D3D11_TEXTURE2D_DESC Desc)
like this :
...
m_DeviceContext->Draw(NUMVERTICES * DirtyCount, 0);
BYTE *Bytes = GetImageData(m_Device, m_DeviceContext, SrcSurface, Desc);
Thank you all

How to add color palette to BITMAPINFO

I have a function which creates a bmp file and writes the file header, the info header, and the actual pixel data respectively. Here it is:
bool SaveBMP(BYTE* Buffer, int width, int height, long paddedsize, LPCTSTR bmpfile)
{
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
memset(&info, 0, sizeof(BITMAPINFOHEADER));
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paddedsize;
bmfh.bfOffBits = 0x36; // number of bytes to start of bitmap bits
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = width;
info.biHeight = height;
info.biPlanes = 1;
info.biBitCount = 8;
info.biCompression = 0;
info.biSizeImage = 0;
info.biXPelsPerMeter = 0;
info.biYPelsPerMeter = 0;
info.biClrUsed = 256;
info.biClrImportant = 0;
HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// write file header
unsigned long bwritten;
if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// write infoheader
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// write image data
if (WriteFile(file, Buffer, paddedsize, &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// and close file
CloseHandle(file);
return true;
}
However, I understand that I have to provide a color palette for 8bit grayscale images like in the following code.
BITMAPINFO* pbmi
for (int i = 0; i<256; i++)
{
pbmi->bmiColors[i].rgbRed = i;
pbmi->bmiColors[i].rgbGreen = i;
pbmi->bmiColors[i].rgbBlue = i;
pbmi->bmiColors[i].rgbReserved = 0;
}
The problem is, I don't know how to connect my BITMAPINFOHEADER to the BITMAPINFO.
And is there a way to use CreateDIBSection function with my current code?
I modified the function and didn't use BITMAPINFO at all. I used RGBQUAD to write the color palette instead. Here's the solution:
bool SaveBMP(BYTE* Buffer, int width, int height, long paddedsize, LPCTSTR bmpfile)
{
const int NUMBER_OF_COLORS = 256;
const int COLOR_PALETTE_SIZE = NUMBER_OF_COLORS * sizeof(RGBQUAD);
const int HEADER_OFFSET = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + COLOR_PALETTE_SIZE;
const int TOTAL_FILE_SIZE = HEADER_OFFSET + paddedsize;
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
RGBQUAD quad[NUMBER_OF_COLORS];
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
memset(&info, 0, sizeof(BITMAPINFOHEADER));
// create the color palette
for (int i = 0; i < NUMBER_OF_COLORS; i++)
{
quad[i].rgbBlue = i;
quad[i].rgbGreen = i;
quad[i].rgbRed = i;
quad[i].rgbReserved = 0;
}
// fill the fileheader
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfSize = TOTAL_FILE_SIZE; // Total file size
bmfh.bfReserved1 = 0; // UNUSED
bmfh.bfReserved2 = 0; // UNUSED
bmfh.bfOffBits = HEADER_OFFSET; // Offset to start of pixel data
// fill the infoheader
info.biSize = sizeof(BITMAPINFOHEADER); // Header size (Must be at least 40)
info.biWidth = width; // Image width
info.biHeight = -height; // Image height
info.biPlanes = 1; // MUST BE 1
info.biBitCount = 8; // Bits per pixel (1, 4, 8, 16, 24 or 32)
info.biCompression = 0; // Compression type (BI_RGB = 0, BI_RLE8 = 1, BI_RLE4 = 2 or BI_BITFIELDS = 3)
info.biSizeImage = height * width; // Image size (May be 0 if not compressed)
info.biXPelsPerMeter = 0; // Preferred resolution in pixels per meter
info.biYPelsPerMeter = 0; // Preferred resolution in pixels per meter
info.biClrUsed = NUMBER_OF_COLORS; // Number of entries in the color map that are actually used
info.biClrImportant = 0; // Number of significant colors (All colors = 0)
// open the file to write to
HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
{
CloseHandle(file);
return false;
}
// write file header
unsigned long bwritten;
if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// write info header
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// write palette
if (WriteFile(file, quad, COLOR_PALETTE_SIZE, &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// write image data
if (WriteFile(file, Buffer, paddedsize, &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
// close file
CloseHandle(file);
return true;
}

ffmpeg sws_scale got distorted image from YUV420P to RGB24

I got distorted image when try to convert YUV420p to RGB24 using
sws_scale.
Code:
ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
fprintf(stderr, "Error decoding video frame\n");
return ret;
}
if (*got_frame)
{
printf("video_frame%s n:%d coded_n:%d pts:%s\n",
cached ? "(cached)" : "",
video_frame_count++, frame->coded_picture_number,
"#"/*av_ts2timestr(frame->pts, &video_dec_ctx->time_base)*/);
/* copy decoded frame to destination buffer:
* this is required since rawvideo expects non aligned data */
av_image_copy(video_dst_data, video_dst_linesize,
(const uint8_t **)(frame->data), frame->linesize,
video_dec_ctx->pix_fmt, video_dec_ctx->width, video_dec_ctx->height);
/* write to rawvideo file */
fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);
AVPicture pic;
avpicture_alloc( &pic, AV_PIX_FMT_RGB24, frame->width, frame->height);
SwsContext *ctxt = sws_getContext(frame->width, frame->height, static_cast<AVPixelFormat>(frame->format),
frame->width, frame->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);
if ( NULL == ctxt )
{
//Log("failed to get sws context");
}
if ( 0 < sws_scale(ctxt, frame->data, frame->linesize, 0, frame->height, pic.data, pic.linesize))
{
char szPic[256] = { 0 };
sprintf( szPic, "decoded/%d.bmp", video_frame_count );
FILE *pf = fopen(szPic,"w");
if ( NULL != pf )
{
BITMAPFILEHEADER bmpFileHeader = {0};
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfType = 0x4D42;
bmpFileHeader.bfSize = sizeof(bmpFileHeader) + sizeof(BITMAPINFOHEADER) + pic.linesize[0] * frame->height;
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + sizeof(BITMAPINFOHEADER);
BITMAPINFOHEADER bmiHeader = { 0 };
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = frame->width;
bmiHeader.biHeight = 0 - frame->height;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = pic.linesize[0] * frame->height;
bmiHeader.biXPelsPerMeter = 0;
bmiHeader.biYPelsPerMeter = 0;
bmiHeader.biClrUsed = 0;
bmiHeader.biClrImportant = 0;
fwrite( &bmpFileHeader, 1, sizeof(bmpFileHeader), pf );
fwrite( &bmiHeader, 1, sizeof(bmiHeader), pf );
fwrite( pic.data[0], 1, pic.linesize[0] * frame->height, pf );
fclose( pf );
}
}
// pic.data[0] now contains the image data in RGB format (3 bytes)
// and pic.linesize[0] is the pitch of the data (ie. size of a row in memory, which can be larger than width*sizeof(pixel))
avpicture_free(&pic);
sws_freeContext(ctxt);
}
above only decode frame then convert this from to RGB24, then write a bitmap.
original video frame like this,
but converted image,
is there missing some code or some code is wrong?
thanks in advance.
fwrite( pic.data[0], 1, pic.linesize[0] * frame->height, pf );
For an image of e.g. 1280x720, linesize is typically larger, e.g. 1312, so you'll be writing more data than image size if you write linesize*height. You want to write (in a loop) width pixels offset by linesize bytes:
uint8_t *ptr = pic.data[0];
for (int y = 0; y < frame->height; y++) {
fwrite(ptr, 1, frame->width, pf);
ptr += pic.linesize[0];
}
And then it should work correctly.
maybe these codes can help you. these works good.
int got_frame = 0;
auto len = avcodec_decode_video2(m_avCodecContext
, m_avFrame
, &got_frame
, &avpkt);
if (len < 0)
{
return;
}
if (got_frame /*&& !silentMode*/)
{
//if (videoRenderer != nullptr)
{
if (frameSize == NULL)
{
return;
}
uint8_t *dst_data[4];
int dst_linesize[4];
int dst_w, dst_h;
int ret = 0;
if (1)// avcodec_alloc_frame()
{
auto stride = m_avFrame->linesize;
auto scan0 = m_avFrame->data;
SwsContext *scaleContext = sws_getContext(m_avCodecContext->width
, m_avCodecContext->height
, m_avCodecContext->pix_fmt
, m_avCodecContext->width
, m_avCodecContext->height
, PixelFormat::PIX_FMT_BGR24
, SWS_FAST_BILINEAR, NULL, NULL, NULL);
if (scaleContext == NULL)
{
//TODO: log error
return;
}
try
{
//*vb->signal = 1;
ret = avpicture_alloc(&m_dst_picture
, PixelFormat::PIX_FMT_BGR24
, m_avCodecContext->width
, m_avCodecContext->height);
// AVFrame *picture_RGB;
// uint8_t *bufferRGB;
// picture_RGB = avcodec_alloc_frame();
// bufferRGB = (uint8_t*)malloc(720*576*(24/8)/*avpicture_get_size(PIX_FMT_RGB24, 720, 576)*/);
// avpicture_fill((AVPicture *)picture_RGB, bufferRGB, PIX_FMT_RGB24, 720, 576);
if (ret < 0)
{
return;
}
int retScale = sws_scale(scaleContext
, scan0
, stride
, 0
, m_avCodecContext->height
, m_dst_picture.data //picture_RGB->data
, m_dst_picture.linesize //picture_RGB->linesize
);
if (1)
{
HWND hwnd = m_pParent->GetSafeHwnd();
SetFocus(hwnd);
CRect rc;
m_pParent->GetClientRect(rc);
CDC *cdc = m_pParent->GetDC();
char* bitmap = (char*)m_dst_picture.data[0];
// static unsigned int i = 0;
// bmp_save(bitmap, m_avCodecContext->width, m_avCodecContext->height, i++);
BITMAPINFO bmpinfo;
bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpinfo.bmiHeader.biWidth = m_avCodecContext->width;
bmpinfo.bmiHeader.biHeight = -m_avCodecContext->height;
bmpinfo.bmiHeader.biPlanes = 1;
bmpinfo.bmiHeader.biBitCount = 24;
bmpinfo.bmiHeader.biCompression = BI_RGB;
bmpinfo.bmiHeader.biSizeImage =
m_avCodecContext->width * m_avCodecContext->height * (24 / 8);
bmpinfo.bmiHeader.biXPelsPerMeter = 100;
bmpinfo.bmiHeader.biYPelsPerMeter = 100;
bmpinfo.bmiHeader.biClrUsed = 0;
bmpinfo.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap = CreateDIBitmap(cdc->GetSafeHdc(), &bmpinfo.bmiHeader, CBM_INIT, bitmap, &bmpinfo/*bi*/, DIB_RGB_COLORS);
DrawBitmap(cdc, hBitmap, m_pParent);
::DeleteObject(hBitmap);
::DeleteObject(cdc->GetSafeHdc());
}
avpicture_free(&m_dst_picture);
sws_freeContext(scaleContext);
}
catch (int e)
{
sws_freeContext(scaleContext);
}
}
}
}
void DrawBitmap(CDC *pDC, HBITMAP hbitmap,CWnd *wnd)
{
CBitmap *pBitmap = CBitmap::FromHandle(hbitmap);
BITMAP bm;
pBitmap -> GetBitmap(&bm);
CDC MemDC;
MemDC.CreateCompatibleDC(pDC);
HGDIOBJ gob= MemDC.SelectObject(pBitmap);
CRect rc;
wnd->GetClientRect(rc);
pDC->SetStretchBltMode( COLORONCOLOR);
pDC->StretchBlt(0, 0,rc.Width(),rc.Height() , &MemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
MemDC.SelectObject(gob);
DeleteObject(pBitmap);
DeleteObject(MemDC);
DeleteObject(&bm);
ReleaseDC(wnd->GetSafeHwnd(), MemDC);
}
void initDecoder()
{
m_avCodecContext = avcodec_alloc_context();
if (!m_avCodecContext)
{
//failed to allocate codec context
Cleanup();
return;
}
m_avCodecContext->flags = 0;
uint8_t startCode[] = { 0x00, 0x00, 0x01 };
//////////////////////////////////////////////////////////////////////////
//I thought for play live video you can comment these lines.
if (m_sProps != NULL)
{
// USES_CONVERSION;
// ::MessageBox(NULL, A2T(sprops), TEXT("sprops"), MB_OK);
unsigned spropCount;
SPropRecord* spropRecords = parseSPropParameterSets(m_sProps, spropCount);
try
{
for (unsigned i = 0; i < spropCount; ++i)
{
AddExtraData(startCode, sizeof(startCode));
AddExtraData(spropRecords[i].sPropBytes, spropRecords[i].sPropLength);
}
}
catch (void*)
{
//extradata exceeds size limit
delete[] spropRecords;
Cleanup();
return;
}
delete[] spropRecords;
m_avCodecContext->extradata = extraDataBuffer;
m_avCodecContext->extradata_size = extraDataSize;
}
AddExtraData(startCode, sizeof(startCode));
bInitEx = true;
av_register_all();
avcodec_register_all();
m_codecId = CODEC_ID_H264;
m_avCodec = avcodec_find_decoder(m_codecId);
if (m_avCodec == NULL)
{
return;
}
if (avcodec_open(m_avCodecContext, m_avCodec) < 0)
{
//failed to open codec
Cleanup();
return;
}
if (m_avCodecContext->codec_id == CODEC_ID_H264)
{
m_avCodecContext->flags2 |= CODEC_FLAG2_CHUNKS;
//avCodecContext->flags2 |= CODEC_FLAG2_SHOW_ALL;
}
m_avFrame = avcodec_alloc_frame();
if (!m_avFrame)
{
//failed to allocate frame
Cleanup();
return;
}
}