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

refactor(video): use generics to simply VideoFrame conversion functions

This commit replaces the contents of toQImage(), toToxAVFrame() and
getAVFrame() with speciailized calls to a generic toGenericObject()
function for better code clarity.
This commit is contained in:
initramfs 2016-05-19 20:49:39 +08:00 committed by initramfs
parent 897cec4e75
commit 6d18c109e8
No known key found for this signature in database
GPG Key ID: 78B8BDF87E9EF0AF
2 changed files with 81 additions and 109 deletions

View File

@ -155,113 +155,45 @@ void VideoFrame::releaseFrame()
const AVFrame* VideoFrame::getAVFrame(QSize frameSize, const int pixelFormat, const bool requireAligned)
{
frameLock.lockForRead();
// We return nullptr if the VideoFrame is no longer valid
if(frameBuffer.size() == 0)
// Since we are retrieving the AVFrame* directly, we merely need to pass the arguement through
const std::function<AVFrame*(AVFrame* const)> converter = [](AVFrame* const frame)
{
frameLock.unlock();
return nullptr;
}
return frame;
};
if(frameSize.width() == 0 && frameSize.height() == 0)
{
frameSize = sourceDimensions.size();
}
// We need an explicit null pointer holding object to pass to toGenericObject()
AVFrame* nullPointer = nullptr;
AVFrame* ret = retrieveAVFrame(frameSize, pixelFormat, requireAligned);
if(ret)
{
frameLock.unlock();
return ret;
}
// VideoFrame does not contain an AVFrame to spec, generate one here
ret = generateAVFrame(frameSize, pixelFormat, requireAligned);
/*
* We need to "upgrade" the lock to a write lock so we can update our frameBuffer map.
*
* It doesn't matter if another thread obtains the write lock before we finish since it is
* likely writing to somewhere else. Worst-case scenario, we merely perform the generation
* process twice, and discard the old result.
*/
frameLock.unlock();
frameLock.lockForWrite();
storeAVFrame(ret, frameSize, pixelFormat);
frameLock.unlock();
return ret;
// Returns std::nullptr case of invalid generation
return toGenericObject(frameSize, pixelFormat, requireAligned, converter, nullPointer);
}
QImage VideoFrame::toQImage(QSize frameSize)
{
frameLock.lockForRead();
// We return an empty QImage if the VideoFrame is no longer valid
if(frameBuffer.size() == 0)
{
frameLock.unlock();
return QImage {};
}
if(frameSize.width() == 0 && frameSize.height() == 0)
{
frameSize = sourceDimensions.size();
}
AVFrame* frame = retrieveAVFrame(frameSize, static_cast<int>(AV_PIX_FMT_RGB24), false);
if(frame)
// Converter function (constructs QImage out of AVFrame*)
const std::function<QImage(AVFrame* const)> converter = [&](AVFrame* const frame)
{
QImage ret {*(frame->data), frameSize.width(), frameSize.height(), *(frame->linesize), QImage::Format_RGB888};
return QImage {*(frame->data), frameSize.width(), frameSize.height(), *(frame->linesize), QImage::Format_RGB888};
};
frameLock.unlock();
return ret;
}
// VideoFrame does not contain an AVFrame to spec, generate one here
frame = generateAVFrame(frameSize, static_cast<int>(AV_PIX_FMT_RGB24), false);
/*
* We need to "upgrade" the lock to a write lock so we can update our frameBuffer map.
*
* It doesn't matter if another thread obtains the write lock before we finish since it is
* likely writing to somewhere else. Worst-case scenario, we merely perform the generation
* process twice, and discard the old result.
*/
frameLock.unlock();
frameLock.lockForWrite();
storeAVFrame(frame, frameSize, static_cast<int>(AV_PIX_FMT_RGB24));
QImage ret {*(frame->data), frameSize.width(), frameSize.height(), *(frame->linesize), QImage::Format_RGB888};
frameLock.unlock();
return ret;
// Returns an empty constructed QImage in case of invalid generation
return toGenericObject(frameSize, AV_PIX_FMT_RGB24, false, converter, QImage {});
}
ToxAVFrame VideoFrame::toToxAVFrame(QSize frameSize)
{
frameLock.lockForRead();
// We return nullptr if the VideoFrame is no longer valid
if(frameBuffer.size() == 0)
{
frameLock.unlock();
return {0, 0, nullptr, nullptr, nullptr};
}
if(frameSize.width() == 0 && frameSize.height() == 0)
{
frameSize = sourceDimensions.size();
}
AVFrame* frame = retrieveAVFrame(frameSize, static_cast<int>(AV_PIX_FMT_YUV420P), true);
if(frame)
// Converter function (constructs ToxAVFrame out of AVFrame*)
const std::function<ToxAVFrame(AVFrame* const)> converter = [&](AVFrame* const frame)
{
ToxAVFrame ret
{
@ -270,34 +202,10 @@ ToxAVFrame VideoFrame::toToxAVFrame(QSize frameSize)
frame->data[0], frame->data[1], frame->data[2]
};
frameLock.unlock();
return ret;
}
// VideoFrame does not contain an AVFrame to spec, generate one here
frame = generateAVFrame(frameSize, static_cast<int>(AV_PIX_FMT_YUV420P), true);
/*
* We need to "upgrade" the lock to a write lock so we can update our frameBuffer map.
*
* It doesn't matter if another thread obtains the write lock before we finish since it is
* likely writing to somewhere else. Worst-case scenario, we merely perform the generation
* process twice, and discard the old result.
*/
frameLock.unlock();
frameLock.lockForWrite();
storeAVFrame(frame, frameSize, static_cast<int>(AV_PIX_FMT_YUV420P));
ToxAVFrame ret
{
static_cast<std::uint16_t>(frameSize.width()),
static_cast<std::uint16_t>(frameSize.height()),
frame->data[0], frame->data[1], frame->data[2]
};
frameLock.unlock();
return ret;
return toGenericObject(frameSize, AV_PIX_FMT_YUV420P, true, converter, ToxAVFrame {0, 0, nullptr, nullptr, nullptr});
}
AVFrame* VideoFrame::retrieveAVFrame(const QSize& dimensions, const int pixelFormat, const bool requireAligned)

View File

@ -387,6 +387,70 @@ private:
*/
void deleteFrameBuffer();
/**
* @brief Converts this VideoFrame to a generic type T based on the given parameters and
* supplied converter functions.
*
* This function is used internally to create various toXObject functions that all follow the
* same generation pattern (where XObject is some existing type like QImage).
*
* In order to create such a type, a object constructor function is required that takes the
* generated AVFrame object and creates type T out of it. This function additionally requires
* a null object of type T that represents an invalid/null object for when the generation
* process fails (e.g. when the VideoFrame is no longer valid).
*
* @param dimensions the dimensions of the frame.
* @param pixelFormat the pixel format of the frame.
* @param requireAligned true if the generated frame needs to be frame aligned, false otherwise.
* @param objectConstructor a std::function that takes the generated AVFrame and converts it
* to an object of type T.
* @param nullObject an object of type T that represents the null/invalid object to be used
* when the generation process fails.
*/
template <typename T>
T toGenericObject(const QSize& dimensions, const int pixelFormat, const bool requireAligned,
const std::function<T(AVFrame* const)> objectConstructor, const T& nullObject)
{
frameLock.lockForRead();
// We return nullObject if the VideoFrame is no longer valid
if(frameBuffer.size() == 0)
{
frameLock.unlock();
return nullObject;
}
AVFrame* frame = retrieveAVFrame(dimensions, static_cast<int>(pixelFormat), requireAligned);
if(frame)
{
T ret = objectConstructor(frame);
frameLock.unlock();
return ret;
}
// VideoFrame does not contain an AVFrame to spec, generate one here
frame = generateAVFrame(dimensions, static_cast<int>(pixelFormat), requireAligned);
/*
* We need to "upgrade" the lock to a write lock so we can update our frameBuffer map.
*
* It doesn't matter if another thread obtains the write lock before we finish since it is
* likely writing to somewhere else. Worst-case scenario, we merely perform the generation
* process twice, and discard the old result.
*/
frameLock.unlock();
frameLock.lockForWrite();
storeAVFrame(frame, dimensions, static_cast<int>(pixelFormat));
T ret = objectConstructor(frame);
frameLock.unlock();
return ret;
}
private:
// ID
const IDType frameID;