From 8d3aef926931aaa4fa61f650834688f87b12202c Mon Sep 17 00:00:00 2001 From: initramfs Date: Mon, 1 Aug 2016 13:45:05 +0800 Subject: [PATCH] refactor(video): update documentation to match new format (issue #3559) --- src/video/videoframe.cpp | 175 ++++++++++++++++++++++++++++++++++++++ src/video/videoframe.h | 175 +------------------------------------- src/video/videosource.cpp | 8 ++ src/video/videosource.h | 27 +++--- 4 files changed, 196 insertions(+), 189 deletions(-) diff --git a/src/video/videoframe.cpp b/src/video/videoframe.cpp index 5d370c5d2..f22f505c8 100644 --- a/src/video/videoframe.cpp +++ b/src/video/videoframe.cpp @@ -24,6 +24,57 @@ extern "C"{ #include } +/** + * @struct ToxYUVFrame + * @brief A simple structure to represent a ToxYUV video frame (corresponds to a frame encoded + * under format: AV_PIX_FMT_YUV420P [FFmpeg] or VPX_IMG_FMT_I420 [WebM]). + * + * This structure exists for convenience and code clarity when ferrying YUV420 frames from one + * source to another. The buffers pointed to by the struct should not be owned by the struct nor + * should they be freed from the struct, instead this struct functions only as a simple alias to a + * more complicated frame container like AVFrame. + * + * The creation of this structure was done to replace existing code which mis-used vpx_image + * structs when passing frame data to toxcore. + */ + +/** + * @class VideoFrame + * @brief An ownernship and management class for AVFrames. + * + * VideoFrame takes ownership of an AVFrame* and allows fast conversions to other formats. + * Ownership of all video frame buffers is kept by the VideoFrame, even after conversion. All + * references to the frame data become invalid when the VideoFrame is deleted. We try to avoid + * pixel format conversions as much as possible, at the cost of some memory. + * + * Every function in this class is thread safe apart from concurrent construction and deletion of + * the object. + * + * This class uses the phrase "frame alignment" to specify the property that each frame's width is + * equal to it's maximum linesize. Note: this is NOT "data alignment" which specifies how allocated + * buffers are aligned in memory. Though internally the two are related, unless otherwise specified + * all instances of the term "alignment" exposed from public functions refer to frame alignment. + * + * Frame alignment is an important concept because ToxAV does not support frames with linesizes not + * directly equal to the width. + */ + +/** + * @var dataAlignment + * @brief Data alignment parameter used to populate AVFrame buffers. + * + * This field is public in effort to standardized the data alignment parameter for all AVFrame + * allocations. + * + * It's currently set to 32-byte alignment for AVX2 support. + */ + +/** + * @class FrameBufferKey + * @brief A class representing a structure that stores frame properties to be used as the key + * value for a std::unordered_map. + */ + // Initialize static fields VideoFrame::AtomicIDType VideoFrame::frameIDs {0}; @@ -32,6 +83,15 @@ std::unordered_mapwidth, sourceFrame->height}, sourceFrame->format, freeSourceFrame){} +/** + * @brief Destructor for VideoFrame. + */ VideoFrame::~VideoFrame() { // Release frame @@ -70,6 +133,14 @@ VideoFrame::~VideoFrame() refsLock.unlock(); } +/** + * @brief Returns the validity of this VideoFrame. + * + * A VideoFrame is valid if it manages at least one AVFrame. A VideoFrame can be invalidated + * by calling releaseFrame() on it. + * + * @return true if the VideoFrame is valid, false otherwise. + */ bool VideoFrame::isValid() { frameLock.lockForRead(); @@ -79,6 +150,14 @@ bool VideoFrame::isValid() return retValue; } +/** + * @brief Causes the VideoFrame class to maintain an internal reference for the frame. + * + * The internal reference is managed via a std::weak_ptr such that it doesn't inhibit + * destruction of the object once all external references are no longer reachable. + * + * @return a std::shared_ptr holding a reference to this frame. + */ std::shared_ptr VideoFrame::trackFrame() { // Add frame to tracked reference list @@ -105,6 +184,17 @@ std::shared_ptr VideoFrame::trackFrame() return ret; } +/** + * @brief Untracks all the frames for the given VideoSource, releasing them if specified. + * + * This function causes all internally tracked frames for the given VideoSource to be dropped. + * If the releaseFrames option is set to true, the frames are sequentially released on the + * caller's thread in an unspecified order. + * + * @param sourceID the ID of the VideoSource to untrack frames from. + * @param releaseFrames true to release the frames as necessary, false otherwise. Defaults to + * false. + */ void VideoFrame::untrackFrames(const VideoFrame::IDType& sourceID, bool releaseFrames) { refsLock.lockForWrite(); @@ -144,6 +234,9 @@ void VideoFrame::untrackFrames(const VideoFrame::IDType& sourceID, bool releaseF refsLock.unlock(); } +/** + * @brief Releases all frames managed by this VideoFrame and invalidates it. + */ void VideoFrame::releaseFrame() { frameLock.lockForWrite(); @@ -153,6 +246,19 @@ void VideoFrame::releaseFrame() frameLock.unlock(); } +/** + * @brief Retrieves an AVFrame derived from the source based on the given parameters. + * + * If a given frame does not exist, this function will perform appropriate conversions to + * return a frame that fulfills the given parameters. + * + * @param frameSize the dimensions of the frame to get. If frame size is 0, defaults to source + * frame size. + * @param pixelFormat the desired pixel format of the frame. + * @param requireAligned true if the returned frame must be frame aligned, false if not. + * @return a pointer to a AVFrame with the given parameters or nullptr if the VideoFrame is no + * longer valid. + */ const AVFrame* VideoFrame::getAVFrame(QSize frameSize, const int pixelFormat, const bool requireAligned) { // Since we are retrieving the AVFrame* directly, we merely need to pass the arguement through @@ -168,6 +274,17 @@ const AVFrame* VideoFrame::getAVFrame(QSize frameSize, const int pixelFormat, co return toGenericObject(frameSize, pixelFormat, requireAligned, converter, nullPointer); } +/** + * @brief Converts this VideoFrame to a QImage that shares this VideoFrame's buffer. + * + * The VideoFrame will be scaled into the RGB24 pixel format along with the given + * dimension. + * + * @param frameSize the given frame size of QImage to generate. If frame size is 0, defaults to + * source frame size. + * @return a QImage that represents this VideoFrame, sharing it's buffers or a null image if + * this VideoFrame is no longer valid. + */ QImage VideoFrame::toQImage(QSize frameSize) { if(frameSize.width() == 0 && frameSize.height() == 0) @@ -185,6 +302,17 @@ QImage VideoFrame::toQImage(QSize frameSize) return toGenericObject(frameSize, AV_PIX_FMT_RGB24, false, converter, QImage {}); } +/** + * @brief Converts this VideoFrame to a ToxAVFrame that shares this VideoFrame's buffer. + * + * The given ToxAVFrame will be frame aligned under a pixel format of planar YUV with a chroma + * subsampling format of 4:2:0 (i.e. AV_PIX_FMT_YUV420P). + * + * @param frameSize the given frame size of ToxAVFrame to generate. If frame size is 0, defaults + * to source frame size. + * @return a ToxAVFrame structure that represents this VideoFrame, sharing it's buffers or an + * empty structure if this VideoFrame is no longer valid. + */ ToxYUVFrame VideoFrame::toToxAVFrame(QSize frameSize) { if(frameSize.width() == 0 && frameSize.height() == 0) @@ -208,6 +336,21 @@ ToxYUVFrame VideoFrame::toToxAVFrame(QSize frameSize) return toGenericObject(frameSize, AV_PIX_FMT_YUV420P, true, converter, ToxYUVFrame {0, 0, nullptr, nullptr, nullptr}); } +/** + * @brief Retrieves an AVFrame derived from the source based on the given parameters without + * obtaining a lock. + * + * This function is not thread-safe and must be called from a thread-safe context. + * + * Note: this function differs from getAVFrame() in that it returns a nullptr if no frame was + * found. + * + * @param dimensions the dimensions of the frame. + * @param pixelFormat the desired pixel format of the frame. + * @param requireAligned true if the frame must be frame aligned, false otherwise. + * @return a pointer to a AVFrame with the given parameters or nullptr if no such frame was + * found. + */ AVFrame* VideoFrame::retrieveAVFrame(const QSize& dimensions, const int pixelFormat, const bool requireAligned) { if(!requireAligned) @@ -236,6 +379,16 @@ AVFrame* VideoFrame::retrieveAVFrame(const QSize& dimensions, const int pixelFor } } +/** + * @brief Generates an AVFrame based on the given specifications. + * + * This function is not thread-safe and must be called from a thread-safe context. + * + * @param dimensions the required dimensions for the frame. + * @param pixelFormat the required pixel format for the frame. + * @param requireAligned true if the generated frame needs to be frame aligned, false otherwise. + * @return an AVFrame with the given specifications. + */ AVFrame* VideoFrame::generateAVFrame(const QSize& dimensions, const int pixelFormat, const bool requireAligned) { AVFrame* ret = av_frame_alloc(); @@ -299,6 +452,15 @@ AVFrame* VideoFrame::generateAVFrame(const QSize& dimensions, const int pixelFor return ret; } +/** + * @brief Stores a given AVFrame within the frameBuffer map. + * + * This function is not thread-safe and must be called from a thread-safe context. + * + * @param frame the given frame to store. + * @param dimensions the dimensions of the frame. + * @param pixelFormat the pixel format of the frame. + */ void VideoFrame::storeAVFrame(AVFrame* frame, const QSize& dimensions, const int pixelFormat) { FrameBufferKey frameKey = getFrameKey(dimensions, pixelFormat, frame->linesize[0]); @@ -317,6 +479,11 @@ void VideoFrame::storeAVFrame(AVFrame* frame, const QSize& dimensions, const int frameBuffer[frameKey] = frame; } +/** + * @brief Releases all frames within the frame buffer. + * + * This function is not thread-safe and must be called from a thread-safe context. + */ void VideoFrame::deleteFrameBuffer() { for(const auto& frameIterator : frameBuffer) @@ -344,6 +511,14 @@ void VideoFrame::deleteFrameBuffer() frameBuffer.clear(); } +/** + * @brief Constructs a new FrameBufferKey with the given attributes. + * + * @param width the width of the frame. + * @param height the height of the frame. + * @param pixFmt the pixel format of the frame. + * @param lineAligned whether the linesize matches the width of the image. + */ VideoFrame::FrameBufferKey::FrameBufferKey(const int pixFmt, const int width, const int height, const bool lineAligned) : frameWidth(width), frameHeight(height), diff --git a/src/video/videoframe.h b/src/video/videoframe.h index 34831f376..685c9f8f1 100644 --- a/src/video/videoframe.h +++ b/src/video/videoframe.h @@ -36,18 +36,6 @@ extern "C"{ #include #include -/** - * @brief A simple structure to represent a ToxYUV video frame (corresponds to a frame encoded - * under format: AV_PIX_FMT_YUV420P [FFmpeg] or VPX_IMG_FMT_I420 [WebM]). - * - * This structure exists for convenience and code clarity when ferrying YUV420 frames from one - * source to another. The buffers pointed to by the struct should not be owned by the struct nor - * should they be freed from the struct, instead this struct functions only as a simple alias to a - * more complicated frame container like AVFrame. - * - * The creation of this structure was done to replace existing code which mis-used vpx_image - * structs when passing frame data to toxcore. - */ struct ToxYUVFrame { public: @@ -59,25 +47,6 @@ public: const uint8_t* v; }; -/** - * @brief An ownernship and management class for AVFrames. - * - * VideoFrame takes ownership of an AVFrame* and allows fast conversions to other formats. - * Ownership of all video frame buffers is kept by the VideoFrame, even after conversion. All - * references to the frame data become invalid when the VideoFrame is deleted. We try to avoid - * pixel format conversions as much as possible, at the cost of some memory. - * - * Every function in this class is thread safe apart from concurrent construction and deletion of - * the object. - * - * This class uses the phrase "frame alignment" to specify the property that each frame's width is - * equal to it's maximum linesize. Note: this is NOT "data alignment" which specifies how allocated - * buffers are aligned in memory. Though internally the two are related, unless otherwise specified - * all instances of the term "alignment" exposed from public functions refer to frame alignment. - * - * Frame alignment is an important concept because ToxAV does not support frames with linesizes not - * directly equal to the width. - */ class VideoFrame { public: @@ -86,20 +55,9 @@ public: using AtomicIDType = std::atomic_uint_fast64_t; public: - /** - * @brief Constructs a new instance of a VideoFrame, sourced by a given AVFrame pointer. - * @param sourceID the VideoSource's ID to track the frame under. - * @param sourceFrame the source AVFrame pointer to use, must be valid. - * @param dimensions the dimensions of the AVFrame, obtained from the AVFrame if not given. - * @param pixFmt the pixel format of the AVFrame, obtained from the AVFrame if not given. - * @param freeSourceFrame whether to free the source frame buffers or not. - */ VideoFrame(IDType sourceID, AVFrame* sourceFrame, QRect dimensions, int pixFmt, bool freeSourceFrame = false); VideoFrame(IDType sourceID, AVFrame* sourceFrame, bool freeSourceFrame = false); - /** - * Destructor for VideoFrame. - */ ~VideoFrame(); // Copy/Move operations are disabled for the VideoFrame, encapsulate with a std::shared_ptr to manage. @@ -110,58 +68,16 @@ public: const VideoFrame& operator=(const VideoFrame& other) = delete; const VideoFrame& operator=(VideoFrame&& other) = delete; - /** - * @brief Returns the validity of this VideoFrame. - * - * A VideoFrame is valid if it manages at least one AVFrame. A VideoFrame can be invalidated - * by calling releaseFrame() on it. - * - * @return true if the VideoFrame is valid, false otherwise. - */ bool isValid(); - /** - * @brief Causes the VideoFrame class to maintain an internal reference for the frame. - * - * The internal reference is managed via a std::weak_ptr such that it doesn't inhibit - * destruction of the object once all external references are no longer reachable. - * - * @return a std::shared_ptr holding a reference to this frame. - */ std::shared_ptr trackFrame(); - - /** - * @brief Untracks all the frames for the given VideoSource, releasing them if specified. - * - * This function causes all internally tracked frames for the given VideoSource to be dropped. - * If the releaseFrames option is set to true, the frames are sequentially released on the - * caller's thread in an unspecified order. - * - * @param sourceID the ID of the VideoSource to untrack frames from. - * @param releaseFrames true to release the frames as necessary, false otherwise. Defaults to - * false. - */ static void untrackFrames(const IDType& sourceID, bool releaseFrames = false); - /** - * @brief Releases all frames managed by this VideoFrame and invalidates it. - */ void releaseFrame(); - /** - * @brief Retrieves an AVFrame derived from the source based on the given parameters. - * - * If a given frame does not exist, this function will perform appropriate conversions to - * return a frame that fulfills the given parameters. - * - * @param frameSize the dimensions of the frame to get. If frame size is 0, defaults to source - * frame size. - * @param pixelFormat the desired pixel format of the frame. - * @param requireAligned true if the returned frame must be frame aligned, false if not. - * @return a pointer to a AVFrame with the given parameters or nullptr if the VideoFrame is no - * longer valid. - */ const AVFrame* getAVFrame(QSize frameSize, const int pixelFormat, const bool requireAligned); + QImage toQImage(QSize frameSize = {0, 0}); + ToxYUVFrame toToxAVFrame(QSize frameSize = {0, 0}); /** * @brief Returns the ID for the given frame. @@ -202,57 +118,11 @@ public: return sourcePixelFormat; } - /** - * @brief Converts this VideoFrame to a QImage that shares this VideoFrame's buffer. - * - * The VideoFrame will be scaled into the RGB24 pixel format along with the given - * dimension. - * - * @param frameSize the given frame size of QImage to generate. If frame size is 0, defaults to - * source frame size. - * @return a QImage that represents this VideoFrame, sharing it's buffers or a null image if - * this VideoFrame is no longer valid. - */ - QImage toQImage(QSize frameSize = {0, 0}); - - /** - * @brief Converts this VideoFrame to a ToxAVFrame that shares this VideoFrame's buffer. - * - * The given ToxAVFrame will be frame aligned under a pixel format of planar YUV with a chroma - * subsampling format of 4:2:0 (i.e. AV_PIX_FMT_YUV420P). - * - * @param frameSize the given frame size of ToxAVFrame to generate. If frame size is 0, defaults - * to source frame size. - * @return a ToxAVFrame structure that represents this VideoFrame, sharing it's buffers or an - * empty structure if this VideoFrame is no longer valid. - */ - ToxYUVFrame toToxAVFrame(QSize frameSize = {0, 0}); - - /** - * @brief Data alignment parameter used to populate AVFrame buffers. - * - * This field is public in effort to standardized the data alignment parameter for all AVFrame - * allocations. - * - * It's currently set to 32-byte alignment for AVX2 support. - */ static constexpr int dataAlignment = 32; private: - /** - * @brief A class representing a structure that stores frame properties to be used as the key - * value for a std::unordered_map. - */ class FrameBufferKey{ public: - /** - * @brief Constructs a new FrameBufferKey with the given attributes. - * - * @param width the width of the frame. - * @param height the height of the frame. - * @param pixFmt the pixel format of the frame. - * @param lineAligned whether the linesize matches the width of the image. - */ FrameBufferKey(const int width, const int height, const int pixFmt, const bool lineAligned); // Explictly state default constructor/destructor @@ -349,51 +219,10 @@ private: return {frameSize.width(), frameSize.height(), pixFmt, frameAligned}; } - /** - * @brief Retrieves an AVFrame derived from the source based on the given parameters without - * obtaining a lock. - * - * This function is not thread-safe and must be called from a thread-safe context. - * - * Note: this function differs from getAVFrame() in that it returns a nullptr if no frame was - * found. - * - * @param dimensions the dimensions of the frame. - * @param pixelFormat the desired pixel format of the frame. - * @param requireAligned true if the frame must be frame aligned, false otherwise. - * @return a pointer to a AVFrame with the given parameters or nullptr if no such frame was - * found. - */ AVFrame* retrieveAVFrame(const QSize& dimensions, const int pixelFormat, const bool requireAligned); - - /** - * @brief Generates an AVFrame based on the given specifications. - * - * This function is not thread-safe and must be called from a thread-safe context. - * - * @param dimensions the required dimensions for the frame. - * @param pixelFormat the required pixel format for the frame. - * @param requireAligned true if the generated frame needs to be frame aligned, false otherwise. - * @return an AVFrame with the given specifications. - */ AVFrame* generateAVFrame(const QSize& dimensions, const int pixelFormat, const bool requireAligned); - - /** - * @brief Stores a given AVFrame within the frameBuffer map. - * - * This function is not thread-safe and must be called from a thread-safe context. - * - * @param frame the given frame to store. - * @param dimensions the dimensions of the frame. - * @param pixelFormat the pixel format of the frame. - */ void storeAVFrame(AVFrame* frame, const QSize& dimensions, const int pixelFormat); - /** - * @brief Releases all frames within the frame buffer. - * - * This function is not thread-safe and must be called from a thread-safe context. - */ void deleteFrameBuffer(); /** diff --git a/src/video/videosource.cpp b/src/video/videosource.cpp index 9f59acf32..c89f91677 100644 --- a/src/video/videosource.cpp +++ b/src/video/videosource.cpp @@ -19,5 +19,13 @@ #include "videosource.h" +/** + * @class VideoSource + * @brief An abstract source of video frames + * + * When it has at least one subscriber the source will emit new video frames. + * Subscribing is recursive, multiple users can subscribe to the same VideoSource. + */ + // Initialize sourceIDs to 0 VideoSource::AtomicIDType VideoSource::sourceIDs {0}; diff --git a/src/video/videosource.h b/src/video/videosource.h index 040228c44..c3aff9d2a 100644 --- a/src/video/videosource.h +++ b/src/video/videosource.h @@ -27,12 +27,7 @@ class VideoFrame; -/** -@brief An abstract source of video frames -When it has at least one subscriber the source will emit new video frames -Subscribing is recursive, multiple users can subscribe to the same VideoSource -*/ class VideoSource : public QObject { Q_OBJECT @@ -47,26 +42,26 @@ public: virtual ~VideoSource() = default; /** - If subscribe sucessfully opens the source, it will start emitting frameAvailable signals. - */ - virtual bool subscribe() = 0; + * @brief If subscribe sucessfully opens the source, it will start emitting frameAvailable signals. + */ + virtual bool subscribe() = 0; /** - Stop emitting frameAvailable signals, and free associated resources if necessary. - */ + * @brief Stop emitting frameAvailable signals, and free associated resources if necessary. + */ virtual void unsubscribe() = 0; /// ID of this VideoSource const IDType id; signals: /** - Emitted when new frame available to use. - @param frame New frame. - */ + * @brief Emitted when new frame available to use. + * @param frame New frame. + */ void frameAvailable(std::shared_ptr frame); /** - Emitted when the source is stopped for an indefinite amount of time, - but might restart sending frames again later - */ + * @brief Emitted when the source is stopped for an indefinite amount of time, but might restart + * sending frames again later + */ void sourceStopped(); private: /// Used to manage a global ID for all VideoSources