1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

fix(video): fix memory leak caused by unfreed buffers in CoreVideoSource

Fixes a memory leak caused by old code within CoreVideoSource in the
way AVFrame buffers are allocated.
This commit is contained in:
initramfs 2016-04-25 04:11:56 -04:00 committed by initramfs
parent 50f67b3cef
commit 3df6b990ae
No known key found for this signature in database
GPG Key ID: 78B8BDF87E9EF0AF
3 changed files with 34 additions and 29 deletions

View File

@ -70,22 +70,19 @@ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe)
AVFrame* avframe = av_frame_alloc(); AVFrame* avframe = av_frame_alloc();
if (!avframe) if (!avframe)
return; return;
avframe->width = width; avframe->width = width;
avframe->height = height; avframe->height = height;
avframe->format = AV_PIX_FMT_YUV420P; avframe->format = AV_PIX_FMT_YUV420P;
int imgBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1); int bufSize = av_image_alloc(avframe->data, avframe->linesize,
uint8_t* buf = (uint8_t*)av_malloc(imgBufferSize); width, height,
if (!buf) static_cast<AVPixelFormat>(AV_PIX_FMT_YUV420P), VideoFrame::frameAlignment);
{
if(bufSize < 0){
av_frame_free(&avframe); av_frame_free(&avframe);
return; return;
} }
avframe->opaque = buf;
uint8_t** data = avframe->data;
int* linesize = avframe->linesize;
av_image_fill_arrays(data, linesize, buf, AV_PIX_FMT_YUV420P, width, height, 1);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
@ -96,14 +93,13 @@ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe)
for (int j = 0; j < size; j++) for (int j = 0; j < size; j++)
{ {
uint8_t *dst = avframe->data[i] + dstStride * j; uint8_t* dst = avframe->data[i] + dstStride * j;
uint8_t *src = vpxframe->planes[i] + srcStride * j; uint8_t* src = vpxframe->planes[i] + srcStride * j;
memcpy(dst, src, minStride); memcpy(dst, src, minStride);
} }
} }
vframe = std::make_shared<VideoFrame>(avframe); vframe = std::make_shared<VideoFrame>(avframe, true);
emit frameAvailable(vframe); emit frameAvailable(vframe);
} }

View File

@ -24,19 +24,20 @@ extern "C"{
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
} }
VideoFrame::VideoFrame(AVFrame* sourceFrame, QRect dimensions, int pixFmt, std::function<void ()> destructCallback) VideoFrame::VideoFrame(AVFrame* sourceFrame, QRect dimensions, int pixFmt, std::function<void ()> destructCallback, bool freeSourceFrame)
: sourceDimensions(dimensions), : sourceDimensions(dimensions),
sourcePixelFormat(pixFmt), sourcePixelFormat(pixFmt),
freeSourceFrame(freeSourceFrame),
destructCallback(destructCallback) destructCallback(destructCallback)
{ {
frameBuffer[pixFmt][dimensionsToKey(dimensions.size())] = sourceFrame; frameBuffer[pixFmt][dimensionsToKey(dimensions.size())] = sourceFrame;
} }
VideoFrame::VideoFrame(AVFrame* sourceFrame, std::function<void ()> destructCallback) VideoFrame::VideoFrame(AVFrame* sourceFrame, std::function<void ()> destructCallback, bool freeSourceFrame)
: VideoFrame(sourceFrame, QRect {0, 0, sourceFrame->width, sourceFrame->height}, sourceFrame->format, destructCallback){} : VideoFrame(sourceFrame, QRect {0, 0, sourceFrame->width, sourceFrame->height}, sourceFrame->format, destructCallback, freeSourceFrame){}
VideoFrame::VideoFrame(AVFrame* sourceFrame) VideoFrame::VideoFrame(AVFrame* sourceFrame, bool freeSourceFrame)
: VideoFrame(sourceFrame, QRect {0, 0, sourceFrame->width, sourceFrame->height}, sourceFrame->format, nullptr){} : VideoFrame(sourceFrame, QRect {0, 0, sourceFrame->width, sourceFrame->height}, sourceFrame->format, nullptr, freeSourceFrame){}
VideoFrame::~VideoFrame() VideoFrame::~VideoFrame()
{ {
@ -248,6 +249,11 @@ AVFrame* VideoFrame::generateAVFrame(const QSize& dimensions, const int pixelFor
return nullptr; return nullptr;
} }
// Populate AVFrame fields
ret->width = dimensions.width();
ret->height = dimensions.height();
ret->format = pixelFormat;
int bufSize = av_image_alloc(ret->data, ret->linesize, int bufSize = av_image_alloc(ret->data, ret->linesize,
dimensions.width(), dimensions.height(), dimensions.width(), dimensions.height(),
static_cast<AVPixelFormat>(pixelFormat), frameAlignment); static_cast<AVPixelFormat>(pixelFormat), frameAlignment);
@ -267,6 +273,8 @@ AVFrame* VideoFrame::generateAVFrame(const QSize& dimensions, const int pixelFor
resizeAlgo, nullptr, nullptr, nullptr); resizeAlgo, nullptr, nullptr, nullptr);
if(!swsCtx){ if(!swsCtx){
av_freep(&ret->data[0]);
av_frame_unref(ret);
av_frame_free(&ret); av_frame_free(&ret);
return nullptr; return nullptr;
} }
@ -274,13 +282,8 @@ AVFrame* VideoFrame::generateAVFrame(const QSize& dimensions, const int pixelFor
AVFrame* source = frameBuffer[sourcePixelFormat][dimensionsToKey(sourceDimensions.size())]; AVFrame* source = frameBuffer[sourcePixelFormat][dimensionsToKey(sourceDimensions.size())];
sws_scale(swsCtx, source->data, source->linesize, 0, sourceDimensions.height(), ret->data, ret->linesize); sws_scale(swsCtx, source->data, source->linesize, 0, sourceDimensions.height(), ret->data, ret->linesize);
// Populate AVFrame fields
ret->width = dimensions.width();
ret->height = dimensions.height();
ret->format = pixelFormat;
sws_freeContext(swsCtx); sws_freeContext(swsCtx);
return ret; return ret;
} }
@ -292,7 +295,7 @@ void VideoFrame::storeAVFrame(AVFrame* frame, const QSize& dimensions, const int
AVFrame* old_ret = frameBuffer[pixelFormat][dimensionsToKey(dimensions)]; AVFrame* old_ret = frameBuffer[pixelFormat][dimensionsToKey(dimensions)];
// Free old frame // Free old frame
av_freep(&frame->data[0]); av_freep(&old_ret->data[0]);
av_frame_unref(old_ret); av_frame_unref(old_ret);
av_frame_free(&old_ret); av_frame_free(&old_ret);
} }
@ -312,9 +315,13 @@ void VideoFrame::deleteFrameBuffer()
{ {
AVFrame* frame = dimKeyIter.value(); AVFrame* frame = dimKeyIter.value();
// We only need to free allocated buffers for derived frames and not the original source // Treat source frame and derived frames separately
if(pixFmtIter.key() == sourcePixelFormat && dimKeyIter.key() == sourceKey) if(pixFmtIter.key() == sourcePixelFormat && dimKeyIter.key() == sourceKey)
{ {
if(freeSourceFrame)
{
av_freep(&frame->data[0]);
}
av_frame_unref(frame); av_frame_unref(frame);
av_frame_free(&frame); av_frame_free(&frame);
} }

View File

@ -56,10 +56,11 @@ public:
* @param destructCallback callback function to run upon destruction of the VideoFrame * @param destructCallback callback function to run upon destruction of the VideoFrame
* this callback is only run when destroying a valid VideoFrame (e.g. a VideoFrame instance in * this callback is only run when destroying a valid VideoFrame (e.g. a VideoFrame instance in
* which releaseFrame() was called upon it will not call the callback). * which releaseFrame() was called upon it will not call the callback).
* @param freeSourceFrame whether to free the source frame buffers or not.
*/ */
VideoFrame(AVFrame* sourceFrame, QRect dimensions, int pixFmt, std::function<void()> destructCallback); VideoFrame(AVFrame* sourceFrame, QRect dimensions, int pixFmt, std::function<void()> destructCallback, bool freeSourceFrame = false);
VideoFrame(AVFrame* sourceFrame, std::function<void()> destructCallback); VideoFrame(AVFrame* sourceFrame, std::function<void()> destructCallback, bool freeSourceFrame = false);
VideoFrame(AVFrame* sourceFrame); VideoFrame(AVFrame* sourceFrame, bool freeSourceFrame = false);
/** /**
* Destructor for VideoFrame. * Destructor for VideoFrame.
@ -218,6 +219,7 @@ private:
// Source frame // Source frame
const QRect sourceDimensions; const QRect sourceDimensions;
const int sourcePixelFormat; const int sourcePixelFormat;
const bool freeSourceFrame;
// Destructor callback // Destructor callback
const std::function<void ()> destructCallback; const std::function<void ()> destructCallback;