How can I display AVFrame data of FFmpeg in MFC? - mfc

Good morning.
Now, H.264 packet is received from the camera with FFMpeg and decoding succeeded.
And I want to output the decoded data to the screen, but it does not work well.
(We successfully saved the decoded data in PPM, Bitmap file, and the screen was well saved.)
Here is the Source code.
CameraStream.cpp
void CameraStream::Play(CWnd *view)
{
m_View = view;
m_StreamThread = std::thread{
[&, this]()
{
AVPacket packet;
int i = 0;
while (true)
{
{
std::unique_lock<std::mutex> threadMux(this->m_StreamMux);
if (!m_IsStreamAlive)
{
MessageBox(nullptr, _T("ThreadOut"), nullptr, MB_OK);
break;
}
}
int err = av_read_frame(this->m_FormatContext, &packet);
if (err < 0)
{
break;
}
if (packet.stream_index == m_VideoStreamIdx)
{
err = avcodec_send_packet(this->m_VideoCondecContext, &packet);
if (err < 0
|| err == AVERROR(EAGAIN)
|| err == AVERROR_EOF)
{
break;
}
CDC *dc = ((CNetworkVideoViewerView*)m_View)->GetDC();
BYTE *pbmpdata = nullptr;
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biHeight = -(m_VideoCondecContext->height);
bmi.bmiHeader.biWidth = m_VideoCondecContext->width;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biSizeImage = m_VideoCondecContext->height * m_VideoCondecContext->width * 3;
HBITMAP hbmp = CreateDIBSection(dc->GetSafeHdc(), &bmi, DIB_RGB_COLORS, (void**)&pbmpdata, NULL, 0);
AVFrame *frame = av_frame_alloc();;
AVFrame *frameRGB = av_frame_alloc();
uint8_t *buffer = nullptr;
int numBytes = 0;
numBytes = av_image_get_buffer_size(
AV_PIX_FMT_BGR24,
this->m_VideoCondecContext->width,
this->m_VideoCondecContext->height,
1);
buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
int err = av_image_fill_arrays(
frameRGB->data,
frameRGB->linesize,
(uint8_t*)pbmpdata, // Bitmap Pointer
//buffer,
AV_PIX_FMT_BGR24,
this->m_VideoCondecContext->width,
this->m_VideoCondecContext->height,
1);
if (err < 0)
{
break;
}
SwsContext *swsContext = nullptr;
swsContext = sws_getContext(
this->m_VideoCondecContext->width,
this->m_VideoCondecContext->height,
this->m_VideoCondecContext->pix_fmt,
this->m_VideoCondecContext->width,
this->m_VideoCondecContext->height,
AV_PIX_FMT_BGR24,
SWS_BICUBIC,
nullptr,
nullptr,
nullptr);
if (!swsContext)
{
return false;
}
while (err >= 0)
{
err = avcodec_receive_frame(this->m_VideoCondecContext, frame);
if (err == AVERROR(EAGAIN)
|| err == AVERROR(EINVAL)
|| err == AVERROR_EOF)
{
break;
}
if (frame->pkt_size != 0)
{
sws_scale(
swsContext,
(const uint8_t * const *)frame->data,
frame->linesize,
0,
this->m_VideoCondecContext->height,
frameRGB->data,
frameRGB->linesize);
// SendMessage Main Thread.
((CNetworkVideoViewerView*)(m_View))->SendMessage(
UM_DISPLAYBITMAP,
(WPARAM)hbmp);
}
}
av_frame_free(&frame);
av_frame_free(&frameRGB);
sws_freeContext(swsContext);
av_free((void*)buffer);
((CNetworkVideoViewerView*)m_View)->ReleaseDC(dc);
}
av_packet_unref(&packet);
};
} };
};
NetWorkViewerView.cpp
// UM_DISPLAYBITMAP
afx_msg LRESULT CNetworkVideoViewerView::OnUmDisplaybitmap(WPARAM wParam, LPARAM lParam)
{
HBITMAP *hBmp = (HBITMAP*)wParam;
CDC *dc = GetDC();
CDC memDC;
memDC.CreateCompatibleDC(dc);
BITMAP bmpInfo;
GetObject(hBmp, sizeof(BITMAP), &bmpInfo);
memDC.SelectObject(hBmp);
dc->BitBlt(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCCOPY);
DeleteObject(hBmp);
ReleaseDC(dc);
return 0;
}
The above source runs normally without error, but only the black screen is displayed on the application screen.
Why is it created as a bitmap file but not the bitmap you want to display on the screen?
Thank you in advance.
[ 2018. 01. 10 Solved ]
I modified the source as you suggested, and FFmpeg replaces the buffer space with a bitmap address so the screen looks good.
Of course, there are still some areas that need to be fixed, but the screen looks good. Thank you.

Related

Convert wmf file to emf file with SetWinMetaFileBits api in c++

I am trying to convert a wmf file to emf file. From what I've learned on Internet, the best solution looks like this.
BYTE* buffer;
HDC hdc = CreateMetaFileA(filename);
HMETAFILE hmf = CloseMetaFile(hdc);
UINT metasize = GetMetaFileBitsEx(hmf, 0, NULL);
buffer = (BYTE*)malloc(metasize);
HENHMETAFILE hEMF = SetWinMetaFileBits(metasize, buffer, NULL, NULL);
The idea here is to use CreateMetaFileA and CloseMetaFile to get HMETAFILE hmf.
Then I tried my code and the weird thing came. The handle hmf always points ??? in memory and the metasize is always 24 with different pictures. And hEMF is always None.
This is really sad because I spend my whole night on figuring out how to make the code work.
I do read a lot of materials including
http://math2.org/luasearch-2/luadist-extract/cdlua-5.2.dist/src/win32/wmf_emf.c
https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/wmfsave.zip/src/wmfsave.cpp?auto=CPP
Can anyone help me here? Thanks.
You need to initialize the METAFILEPICT structure.
Minimal example:
if (hmf) {
DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL );
if (nSize) {
BYTE *lpvData = new BYTE[nSize];
if (lpvData) {
DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData );
if (dw) {
// Fill out a METAFILEPICT structure
mp.mm = MM_ANISOTROPIC;
mp.xExt = 1000;
mp.yExt = 1000;
mp.hMF = NULL;
// Get a reference DC
hDC = GetDC( NULL );
// Make an enhanced metafile from the windows metafile
hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp );
// Clean up
ReleaseDC( NULL, hDC );
}
delete[] lpvData;
}
DeleteMetaFile( hmf );
}
My test code:
hdcMeta = CreateMetaFile(NULL);
hBrush = CreateSolidBrush(RGB(0, 0, 255));
Rectangle(hdcMeta, 0, 0, 100, 100);
MoveToEx(hdcMeta, 0, 0, NULL);
LineTo(hdcMeta, 100, 100);
MoveToEx(hdcMeta, 0, 100, NULL);
LineTo(hdcMeta, 100, 0);
SelectObject(hdcMeta, hBrush);
Ellipse(hdcMeta, 20, 20, 80, 80);
hmf = CloseMetaFile(hdcMeta);
UINT nSize = GetMetaFileBitsEx(hmf, 0, NULL);
Debug:
You can see nSize = 114
I suspect that you use CreateMetaFileA and CloseMetaFile to directly load the file name and return a handle to a Windows-format metafile is a wrong way.
Updated:
You can get the handle of WMF file in another way.
#include <windows.h>
#include <iostream>
#include <vector>
#pragma pack(1)
typedef struct tagWIN16RECT
{
WORD left;
WORD top;
WORD right;
WORD bottom;
} WIN16RECT;
typedef struct tagPLACEABLEMETAHEADER
{
DWORD key;
WORD hmf;
WIN16RECT bbox;
WORD inch;
DWORD reserved;
WORD checksum;
} PLACEABLEMETAHEADER;
#pragma pack()
HENHMETAFILE WINAPI ConvertWMFToEWMF(IN LPCWSTR lpszMetaFile)
{
HANDLE hFile = ::CreateFileW(
lpszMetaFile,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
return NULL;
DWORD dwSize = ::GetFileSize(hFile, NULL);
std::vector<BYTE> data(dwSize);
DWORD dwRead;
BOOL bSuccess = ::ReadFile(hFile, &data[0], dwSize, &dwRead, NULL);
::CloseHandle(hFile);
HENHMETAFILE hEnhMetaFile = NULL;
if (bSuccess)
{
PLACEABLEMETAHEADER * hdr = (PLACEABLEMETAHEADER*)&data[0];
int iPlaceableHeaderSize = sizeof(PLACEABLEMETAHEADER);
int iOffset = 0;
if (hdr->key != 0x9AC6CDD7) //not placeable header
{
iOffset = 0; //offset remains zero
}
else
{
iOffset = iPlaceableHeaderSize; //file is offset with placeable windows metafile header
}
hEnhMetaFile = ::SetWinMetaFileBits(data.size(), &data[iOffset], NULL, NULL);
if (NULL == hEnhMetaFile)
{
DWORD dwError = GetLastError();
std::cout << "Failed with error code: " << dwError;
}
else
{
std::cout << "Success! Metafile opened and returned as enhanced metafile";
}
}
return hEnhMetaFile;
}
int main()
{
HENHMETAFILE hEMF = ConvertWMFToEWMF(L"C:\\Users\\strives\\Desktop\\AN00010.WMF");
HENHMETAFILE newHEMF = CopyEnhMetaFile(hEMF, L"new EMF.emf");
return 0;
}
This worked for me
CStatic * m_pictCtrl = (CStatic *)this->GetDlgItem(PICT_STATIC);
LPCSTR file = filePath;
ALDUSMFHEADER aldusmfHeader;
DWORD wBytesRead;
double xOri, xExt, yOri, yExt;
HANDLE fh = CreateFileA(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ReadFile(fh, (void *)&aldusmfHeader, ALDUSMFHEADERSIZE, &wBytesRead, NULL);
xOri = aldusmfHeader.bbox.left;
xExt = aldusmfHeader.bbox.right - xOri;
if (aldusmfHeader.bbox.bottom < aldusmfHeader.bbox.top) {
yOri = aldusmfHeader.bbox.bottom;
yExt = aldusmfHeader.bbox.top - aldusmfHeader.bbox.bottom;
}
else {
yOri = aldusmfHeader.bbox.top;
yExt = aldusmfHeader.bbox.top - aldusmfHeader.bbox.bottom;
}
if (wBytesRead == -1 || wBytesRead < ALDUSMFHEADERSIZE)
{
AfxMessageBox(L" is not a placeable Windows metafile : it cannot be converted into EMF format.");
CloseHandle(fh);
return 0;
}
// Envelope in /100 cm
double Density = static_cast<double>(aldusmfHeader.inch);
double Top = static_cast<double>(aldusmfHeader.bbox.top) / Density;
double RawBottom = static_cast<double>(aldusmfHeader.bbox.bottom) / Density;
double Left = static_cast<double>(aldusmfHeader.bbox.left) / Density;
double RawRight = static_cast<double>(aldusmfHeader.bbox.right) / Density;
// In order to correctly import the EMF metafile into WORD, add one delta
double Bottom, Right, Delta, Rate = 0.1;
if (RawBottom > RawRight)
{
Delta = Rate * RawRight;
Right = RawRight + Delta;
Bottom = Right * RawBottom / RawRight;
}
else
{
Delta = Rate * RawBottom;
Bottom = RawBottom + Delta;
Right = Bottom * RawRight / RawBottom;
}
// Metafile header
SetFilePointer(fh, ALDUSMFHEADERSIZE, NULL, FILE_BEGIN);
METAHEADER mfHeader;
ReadFile(fh, (void *)&mfHeader, sizeof(METAHEADER), &wBytesRead, NULL);
// Allocate memory in order to save into memory bits after the Aldus header in the WMF metafile
// * 2 : 16 bits API
DWORD dwSize = mfHeader.mtSize * 2 * sizeof(BYTE);
BYTE *lpMFBits = (BYTE *)malloc(dwSize);
if (lpMFBits == nullptr)
{
AfxMessageBox(L"nullptr lpmfbits");
//cout << "Not enough memory to convert " << WMFFileName << " into EMF format." << endl;
CloseHandle(fh);
return 0;
}
// Bits after the Aldus header
SetFilePointer(fh, ALDUSMFHEADERSIZE, NULL, FILE_BEGIN);
ReadFile(fh, (void *)lpMFBits, dwSize, &wBytesRead, NULL);
if (wBytesRead == -1)
{
//cout << "Error while reading " << WMFFileName << " : impossible to convert it into EMF format." << endl;
free(lpMFBits);
CloseHandle(fh);
return 0;
}
// Save these bits into a memory enhanced metafile
// The memory enhanced metafile only contains 32 bits API functions : TextOut has been converted into ExtTextOutW,
// CreateFontIndirect has been converted into ExtCreateFontIndirectW, ...
METAFILEPICT MetaFilePict;
MetaFilePict.hMF = NULL;
MetaFilePict.mm = MM_ANISOTROPIC;
double Fact = 10.0 * Density;
MetaFilePict.xExt = static_cast<LONG>(Fact * (Right - Left));
MetaFilePict.yExt = static_cast<LONG>(Fact * (Bottom - Top));
HENHMETAFILE hMemoryEnhMetafile = SetWinMetaFileBits(dwSize, lpMFBits, NULL, &MetaFilePict);
free(lpMFBits);
CloseHandle(fh);
if (m_pictCtrl->GetEnhMetaFile() == NULL)
m_pictCtrl->SetEnhMetaFile(hMemoryEnhMetafile);

Memory leak when using av_frame_get_buffer()

I am making a simple video player with ffmpeg. I have noticed that there is a memory leak originating in libavutil. Because ffmpeg is a mature library I assume that I am allocating a new frame incorrectly. The documentation is also vague about freeing the buffer that is created when you call av_frame_get_buffer(). Below is the code I am using to decode the video and queue it up for display on the UI thread.
DWORD WINAPI DecoderThread(LPVOID lpParam)
{
AVFrame *frame = NULL;
AVPacket pkt;
SwsContext *swsCtx = NULL;
UINT8 *buffer = NULL;
INT iNumBytes = 0;
INT result = 0;
frame = av_frame_alloc();
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
// Create scaling context
swsCtx = sws_getContext(codecCtx->width, codecCtx->height, codecCtx->pix_fmt, codecCtx->width, codecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
while (av_read_frame(fmtCtx, &pkt) >= 0) {
if (pkt.stream_index == videoStream) {
result = avcodec_send_packet(codecCtx, &pkt);
while (result >= 0) {
result = avcodec_receive_frame(codecCtx, frame);
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
break;
} else if (result < 0) {
// another error.
}
// Create a new frame to store the RGB24 data.
AVFrame *pFrameRGB = av_frame_alloc();
// Allocate space for the new RGB image.
//av_image_alloc(pFrameRGB->data, pFrameRGB->linesize, codecCtx->width, codecCtx->height, AV_PIX_FMT_BGR24, 1);
// Copy all of the properties from the YUV420P frame.
av_frame_copy_props(pFrameRGB, frame);
pFrameRGB->width = frame->width;
pFrameRGB->height = frame->height;
pFrameRGB->format = AV_PIX_FMT_BGR24;
av_frame_get_buffer(pFrameRGB, 0);
// Convert fram from YUV420P to BGR24 for display.
sws_scale(swsCtx, (const UINT8* const *) frame->data, frame->linesize, 0, codecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
// Queue thr BGR frame for drawing by the main thread.
AddItemToFrameQueue(pFrameRGB);
av_frame_unref(frame);
}
}
while (GetQueueSize() > 100) {
Sleep(10);
}
}
CloseFrameQueue();
av_frame_free(&frame);
avcodec_close(codecCtx);
avformat_close_input(&fmtCtx);
return 0;
}
Is there a better way to allocate a new frame for holding the post sws_scale() transformation?
There is a similar stackoverflow question that uses mostly depreciated function calls. I can't seem to find any answers that conform to the new version of ffmpeg in the documentation. Any help would be appreciated.
Following the suggestions made in the comments I added a av_packet_unref() call to my decoding loop, and it stopped the memory leak issues I was having.
sws_scale(swsCtx, (const UINT8* const *) frame->data, frame->linesize, 0, codecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
// Queue thr BGR frame for drawing by the main thread.
AddItemToFrameQueue(pFrameRGB);
av_frame_unref(frame);
}
av_packet_unref(&pkt);
}
while (GetQueueSize() > 100) {
Sleep(10);
}

Libav AVFrame to Opencv Mat to AVPacket conversion

I am new to libav and I am writing a video manipulation software which uses opencv as its heart. What I did is briefly as below:
1- read the video packet
2- decode the packet into AVFrame
3- convert
the AVFrame to CV Mat
4- manipulate the Mat
5- convert the CV Mat
into AVFrame
6- encode the AVFrame into AVPacket
7- write the packet
8- goto 1
I read dranger tutorial in http://dranger.com/ffmpeg/tutorial01.html and I also used decoding_encoding example. I can read the video, extract video frames and convert them to CV Mat. My problem starts from converting from cv Mat to AVFrame and encode it to AVPacket.
Would you please help me with this?
Here is my code :
int main(int argc, char **argv)
{
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVFrame *pFrameRGB = NULL;
int videoStream=-1;
int audioStream=-1;
int frameFinished;
int numBytes;
uint8_t *buffer = NULL;
struct SwsContext *sws_ctx = NULL;
FrameManipulation *mal_frame;
const char *in_filename, *out_filename;
int ret, i;
if (argc < 3) {
printf("usage: %s input output\n"
"API example program to remux a media file with libavformat and libavcodec.\n"
"The output format is guessed according to the file extension.\n"
"\n", argv[0]);
return 1;
}
in_filename = arg[1];
out_filename = arg[2];
av_register_all();
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO &&
videoStream < 0) {
videoStream=i;
}
if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
audioStream < 0) {
audioStream=i;
}
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
if (ret < 0) {
fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
goto end;
}
out_stream->codec->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
pCodec=avcodec_find_decoder(ifmt_ctx->streams[videoStream]->codec->codec_id);
pCodecCtx = avcodec_alloc_context3(pCodec);
if(avcodec_copy_context(pCodecCtx, ifmt_ctx->streams[videoStream]->codec) != 0) {
fprintf(stderr, "Couldn't copy codec context");
return -1; // Error copying codec context
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
return -1; // Could not open codec
// Allocate video frame
pFrame=av_frame_alloc();
// Allocate an AVFrame structure
pFrameRGB=av_frame_alloc();
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(AV_PIX_FMT_RGB24, ifmt_ctx->streams[videoStream]->codec->width,
ifmt_ctx->streams[videoStream]->codec->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24,
ifmt_ctx->streams[videoStream]->codec->width, ifmt_ctx->streams[videoStream]->codec->height);
av_dump_format(ofmt_ctx, 0, out_filename, 1);
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", out_filename);
goto end;
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24,
ifmt_ctx->streams[videoStream]->codec->width,
ifmt_ctx->streams[videoStream]->codec->height);
// initialize SWS context for software scaling
sws_ctx = sws_getContext(
ifmt_ctx->streams[videoStream]->codec->width,
ifmt_ctx->streams[videoStream]->codec->height,
ifmt_ctx->streams[videoStream]->codec->pix_fmt,
ifmt_ctx->streams[videoStream]->codec->width,
ifmt_ctx->streams[videoStream]->codec->height,
AV_PIX_FMT_BGR24,
SWS_BICUBIC,
NULL,
NULL,
NULL
);
// Loop through packets
while (1) {
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, &pkt);
if(pkt.stream_index==videoStream)
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &pkt);
if(frameFinished) {
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
cv::Mat img= mal_frame->process(
pFrameRGB,pFrame->width,pFrame->height);
/* My problem is Here ------------*/
avpicture_fill((AVPicture*)pFrameRGB,
img.data,
PIX_FMT_BGR24,
outStream->codec->width,
outStream->codec->height);
pFrameRGB->width = ifmt_ctx->streams[videoStream]->codec->width;
pFrameRGB->height = ifmt_ctx->streams[videoStream]->codec->height;
avcodec_encode_video2(ifmt_ctx->streams[videoStream]->codec ,
&pkt , pFrameRGB , &gotPacket);
/*
I get this error
[swscaler # 0x14b58a0] bad src image pointers
[swscaler # 0x14b58a0] bad src image pointers
*/
/* My Problem Ends here ---------- */
}
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
//log_packet(ifmt_ctx, &pkt, "in");
/* copy packet */
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,
AV_ROUND_NEAR_INF);
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
log_packet(ofmt_ctx, &pkt, "out");
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
av_free_packet(&pkt);
}
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
return 1;
}
return 0;
}
When I run this code, I get unknown fatal error in this part:
/* My problem is Here ------------*/
avpicture_fill((AVPicture*)pFrameRGB,
img.data,
PIX_FMT_BGR24,
outStream->codec->width,
outStream->codec->height);
pFrameRGB->width = ifmt_ctx->streams[videoStream]->codec->width;
pFrameRGB->height = ifmt_ctx->streams[videoStream]->codec->height;
avcodec_encode_video2(ifmt_ctx->streams[videoStream]->codec ,
&pkt , pFrameRGB , &gotPacket);
/*
I get this error
[swscaler # 0x14b58a0] bad src image pointers
[swscaler # 0x14b58a0] bad src image pointers
*/
/* My Problem Ends here ---------- */
Here is where I want to convert back cv Mat to AVFrame and encode it to AVPacket. I appreciate your help.
After reading some examples, reading source code and some helps people offered, I managed to make the code run. I used transcoding and encoding examples and mixed them up. Here is my code
here are the highlights:
1- libswscale should be used to convert AVFrame with required packet format to be fed into openCV Mat. To do so, we define
struct SwsContext *sws_ctx = NULL;
sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_BGR24,
SWS_BICUBIC,
NULL,
NULL,
NULL
);
To convert back the opencv Mat to AVFrame, one should again use swscale and translate opencv BGR frame format to YUV. So, I do this:
struct SwsContext *sws_ctx_bgr_yuv = NULL;
sws_ctx_bgr_yuv = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_BGR24,
pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt //AV_PIX_FMT_YUV420p
,0,0,NULL,NULL);
And, here is frame reading/decoding/encoding loop:
while (1) {
if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0)
break;
stream_index = packet.stream_index;
type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type;
av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n",
stream_index);
if (filter_ctx[stream_index].filter_graph) {
av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n");
frame = av_frame_alloc();
if (!frame) {
ret = AVERROR(ENOMEM);
break;
}
av_packet_rescale_ts(&packet,
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base);
dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :
avcodec_decode_audio4;
ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame,
&got_frame, &packet);
if (ret < 0) {
av_frame_free(&frame);
av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");
break;
}
if (got_frame) {
if(stream_index==video_index){
sws_scale(sws_ctx, (uint8_t const * const *)frame->data,
frame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
/*------------------------------------------------------------------------
/* Frame converts to opencv Mat
/*------------------------------------------------------------------------*/
cv::Mat img(frame->height,frame->width,CV_8UC3,pFrameRGB->data[0]);
img=manipulate_image(img); //this is opencv Mat, do whatever you want, but don't change its dimensions and format
//manipulate_function can be considered as as simple as blurring
const int stride[] = {img.step[0] };
/* opencv Mat converts back to AVFrame */
sws_scale(sws_ctx_bgr_yuv, &img.data, stride, 0, img.rows, frame->data, frame->linesize);
}
frame->pts = av_frame_get_best_effort_timestamp(frame);
/* AVFrame re-encodes to AVPacket and will be sent to encoder */
ret = filter_encode_write_frame(frame, stream_index);
av_frame_free(&frame);
if (ret < 0)
goto end;
} else {
av_frame_free(&frame);
}
} else {
/* remux this frame without reencoding */
av_packet_rescale_ts(&packet,
ifmt_ctx->streams[stream_index]->time_base,
ofmt_ctx->streams[stream_index]->time_base);
ret = av_interleaved_write_frame(ofmt_ctx, &packet);
if (ret < 0)
goto end;
}
av_free_packet(&packet);
}
Here is another way to convert between cv::Mat and AVframe using c++ based on some code I found and debugged. Please note its specifically for 8 bit 3 channel images but that can be changed by changing AV_PIX_FMT_BGR24 in both functions.
Hope this helps.
AVFrame cvmat_to_avframe(cv::Mat* frame)
{
AVFrame dst;
cv::Size frameSize = frame->size();
AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_RAWVIDEO);
AVFormatContext* outContainer = avformat_alloc_context();
AVStream *outStream = avformat_new_stream(outContainer, encoder);
avcodec_get_context_defaults3(outStream->codec, encoder);
outStream->codec->pix_fmt = AV_PIX_FMT_BGR24;
outStream->codec->width = frame->cols;
outStream->codec->height = frame->rows;
avpicture_fill((AVPicture*)&dst, frame->data, AV_PIX_FMT_BGR24, outStream->codec->width, outStream->codec->height);
dst.width = frameSize.width;
dst.height = frameSize.height;
return dst;
}
cv::Mat avframe_to_cvmat(AVFrame *frame)
{
AVFrame dst;
cv::Mat m;
memset(&dst, 0, sizeof(dst));
int w = frame->width, h = frame->height;
m = cv::Mat(h, w, CV_8UC3);
dst.data[0] = (uint8_t *)m.data;
avpicture_fill( (AVPicture *)&dst, dst.data[0], AV_PIX_FMT_BGR24, w, h);
struct SwsContext *convert_ctx=NULL;
enum AVPixelFormat src_pixfmt = AV_PIX_FMT_BGR24;
enum AVPixelFormat dst_pixfmt = AV_PIX_FMT_BGR24;
convert_ctx = sws_getContext(w, h, src_pixfmt, w, h, dst_pixfmt,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
sws_scale(convert_ctx, frame->data, frame->linesize, 0, h,
dst.data, dst.linesize);
sws_freeContext(convert_ctx);
return m;
}

Microsoft SDK AMCap GetCurrentImage error

I am trying to modify the existing AmCap application, available through Microsoft's SDK Direct Show samples, in order to get an image of the captured stream when I press the space button. Below is the point in which I am handling the space keydown.
case WM_KEYDOWN:
if((GetAsyncKeyState(VK_ESCAPE) & 0x01) && gcap.fCapturing)
{
StopCapture();
if(gcap.fWantPreview)
{
BuildPreviewGraph();
StartPreview();
}
}
else if ((GetAsyncKeyState(VK_SPACE) & 0x01))
{
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)){
hr=pMC->Pause();
if (SUCCEEDED(hr)){
CaptureImage(TEXT("C:\\output.bmp"));
pMC->Run();
pMC->Release();
}else
ErrMsg(TEXT("Failed to pause stream! hr=0x%x"), hr);
}
else
ErrMsg(TEXT("Cannot grab image"));
}
break;
Below is the contents of the CaptureImage function.
HRESULT hr;
SmartPtr<IBasicVideo> pWC;
// If we got here, gcap.pVW is not NULL
ASSERT(gcap.pVW != NULL);
hr = gcap.pVW->QueryInterface(IID_IBasicVideo, (void**)&pWC);
if (pWC)
{
long bufSize;
long *lpCurrImage = NULL;
pWC->GetCurrentImage(&bufSize, NULL);
lpCurrImage = (long *)malloc(bufSize);
//
// Read the current video frame into a byte buffer. The information
// will be returned in a packed Windows DIB and will be allocated
// by the VMR.
hr = pWC->GetCurrentImage(&bufSize, lpCurrImage);
if (SUCCEEDED(hr))
{
HANDLE fh;
BITMAPFILEHEADER bmphdr;
BITMAPINFOHEADER bmpinfo;
DWORD nWritten;
memset(&bmphdr, 0, sizeof(bmphdr));
memset(&bmpinfo, 0, sizeof(bmpinfo));
bmphdr.bfType = ('M' << 8) | 'B';
bmphdr.bfSize = sizeof(bmphdr) + sizeof(bmpinfo) + bufSize;
bmphdr.bfOffBits = sizeof(bmphdr) + sizeof(bmpinfo);
bmpinfo.biSize = sizeof(bmpinfo);
bmpinfo.biWidth = 640;
bmpinfo.biHeight = 480;
bmpinfo.biPlanes = 1;
bmpinfo.biBitCount = 32;
fh = CreateFile(TEXT("C:\\Users\\mike\\Desktop\\output.bmp"),
GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);
WriteFile(fh, &bmpinfo, sizeof(bmpinfo), &nWritten, NULL);
WriteFile(fh, lpCurrImage, bufSize, &nWritten, NULL);
CloseHandle(fh);
ErrMsg(TEXT("Captured current image to %s"), szFile);
return TRUE;
}
else
{
ErrMsg(TEXT("Failed to capture image! hr=0x%x"), hr);
return FALSE;
}
}
return FALSE;
The problem is that when I am trying to run the app, I receive an HRESULT (0x8000ffff) error when the GetCurrentImage function is being called.
On the other hand in case I execute the app through the VS debugger the code works just fine.
I tried to add a Sleep just after the stream pMC->Pause(), assuming that the problem was timing issue but that did not work.
Any ideas would be extremely helpful!
Thank you in advance.

libavcodec video decoding not working

I am trying to decode video encoded with H264. I am sending AVPacket's data and its size to decoder code. there I am trying to decode the frame and display it on a GUI. problem is when I am decoding the frame it is returning same number of frame byte as the size of packet means it is not decompressing the data. Can any one tell what will be the problem. My encoding program is working fine.
here is code for encoding
static struct SwsContext *img_convert_ctx;
pkt.data = NULL;
pkt.size = 0;
avpicture_fill((AVPicture *)srcFrame, frame,AV_PIX_FMT_BGR24, 640, 480);
if(img_convert_ctx == NULL) {
int w = 640;
int h = 480;
img_convert_ctx = sws_getContext(w, h,
AV_PIX_FMT_BGR24, c->width, c->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
if(img_convert_ctx == NULL) {
fprintf(stderr, "Cannot initialize the conversion context!\n");
}
}
sws_scale(img_convert_ctx, srcFrame->data, srcFrame->linesize, 0,480,picture->data, picture->linesize);
fflush(stdout);
picture->pts=counter;
ret = avcodec_encode_video2(c, &pkt, picture, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
}
if (got_output) {
vdec.decode_frame(pkt.data ,pkt.size);
av_free_packet(&pkt);
}
decoder code...
int len ,got_frame;
avpkt.size = data_length;
avpkt.data = frame_buffer;
if(!frame_buffer){
return "frame buffer empty\n";
}
len = avcodec_decode_video2(avctx ,frame ,&got_frame ,&avpkt);
if( len < 0){
return "error while decoding\n";
}
if( got_frame ){
static struct SwsContext *img_convert_ctx;
if(img_convert_ctx == NULL) {
img_convert_ctx = sws_getContext(w, h,
PIX_FMT_YUV420P, avctx->width,
avctx->height, PIX_FMT_BGR24,
SWS_BICUBIC, NULL, NULL, NULL);
if(img_convert_ctx == NULL) {
return "Cannot initialize the conversion context!\n";
}
}
j=sws_scale(img_convert_ctx,
frame->data , frame->linesize ,
0, h ,picture->data,
picture->linesize );
if(j==0){
exit(1);
}
I am initializing all other code like AVCodecContext and Codec into other method.
Please help me to find the solution.
The avcodec_decode_video2 function should return the number of bytes processed, not the number of bytes of result picture. You just have to check the value of got_frame to find out when decoded a complete frame.