Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created October 1, 2013 11:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roxlu/68a0e34ef735fe655409 to your computer and use it in GitHub Desktop.
Save roxlu/68a0e34ef735fe655409 to your computer and use it in GitHub Desktop.
#ifndef ROXLU_MEMORY_POOL_H
#define ROXLU_MEMORY_POOL_H
/*
# MemoryPool
This "MemoryPool" is used to preallocate N-video/audio frames. Because
we cannot allocate to much memory while running we need to preallocate a
bunch of frames and reuse this.
The memory pool works closely together with the AVPackets. A AVPacket is
passed into a encoder thread. When the encoder is ready with encoding the
packet, it will call "release()" which decreases the refcount. Multiple encoder
threads can share the same AVPacket: BUT YOU NEED TO CALL `addRef` FOR EACH
ENCODER THREAD YOURSELF BEFORE ADDING TO THE STREAMER/ENCODER INSTANCE. USE
`getFreeVideo/AudioPacket()` TO GET A FREE PACKET. THE RETURNED PACKET WILL
HAVE A REFCOUNT OF 1 BY DEFAULT. IF YOU WANT TO PASS THIS PACKET TO MULTIPLE
ENCODER THREADS YOU NEED TO CALL `addRef()` FOR EACH OF THE OTHER ENCODER
THREADS. SO IF YOU HAVE 5 ENCODER THREAD, YOU USE `getFreeVideo/AudioPacket()`
WHICH RETURNS AN `AVPacket` WITH REFCOUNT 1, SO YOU NEED TO CALL `addRef()`
4 TIMES!!
*/
extern "C" {
# include <uv.h>
}
#include <stdint.h>
#include <streamer/core/EncoderTypes.h>
#include <vector>
class MemoryPool {
public:
MemoryPool();
~MemoryPool();
AVPacket* getFreeVideoPacket(); /* get a free video packet from the pool: IMPORTANT: refcount will be set to 1 for you! If you need to share it with multiple encoder call: pkt->addRef(NUMBER_OF_SHARED_ENCODERS) */
AVPacket* getFreeAudioPacket();
bool allocateVideoFrames(size_t nframes, uint32_t nbytes); /* allocate nframes which will contain nbytes of video data */
/* lock the appropriate mutexes */
void lockVideo();
void unlockVideo();
void lockAudio();
void unlockAudio();
/* ref counting for the AVPackets */
void release(AVPacket* pkt);
void addRef(AVPacket* pkt, int count = 1); /* you can add multiple refcounts by change the count, this is handy when you want to share a AVPacket with multiple encoder threads */
private:
AVPacket* getFreePacket(uint8_t type);
private:
uv_mutex_t audio_mutex;
uv_mutex_t video_mutex;
std::vector<AVPacket*> video_packets;
std::vector<AVPacket*> audio_packets;
};
inline void MemoryPool::lockVideo() {
uv_mutex_lock(&video_mutex);
}
inline void MemoryPool::unlockVideo() {
uv_mutex_unlock(&video_mutex);
}
inline void MemoryPool::lockAudio() {
uv_mutex_lock(&audio_mutex);
}
inline void MemoryPool::unlockAudio() {
uv_mutex_unlock(&audio_mutex);
}
inline void MemoryPool::release(AVPacket* pkt) {
if(pkt->type == AV_TYPE_VIDEO) {
lockVideo();
#if !defined(NDEBUG)
if(pkt->refcount == 0) {
printf("error: trying to release a packet which refcount is zero already! \n");
::exit(EXIT_FAILURE);
}
#endif
pkt->refcount--;
unlockVideo();
}
else if(pkt->type == AV_TYPE_AUDIO) {
lockAudio();
#if !defined(NDEBUG)
if(pkt->refcount == 0) {
printf("error: trying to release a packet which refcount is zero already!\n");
::exit(EXIT_FAILURE);
}
#endif
pkt->refcount--;
unlockAudio();
}
}
inline void MemoryPool::addRef(AVPacket* pkt, int count) {
if(pkt->type == AV_TYPE_VIDEO) {
lockVideo();
pkt->refcount += count;
unlockVideo();
}
else if(pkt->type == AV_TYPE_AUDIO) {
lockAudio();
pkt->refcount += count;
unlockAudio();
}
}
inline AVPacket* MemoryPool::getFreeAudioPacket() {
return getFreePacket(AV_TYPE_AUDIO);
}
inline AVPacket* MemoryPool::getFreeVideoPacket() {
return getFreePacket(AV_TYPE_VIDEO);
}
/*
Get a free AVPacket for the given type.
IMPORTANT: when we find a free packet will set the
refcount to 1 so you don't need to call `AVPacket::addRef()`
anymore. We need to do this because there might be some other
thread that uses this getFreePacket() and it might return the
same packet when we did not set the refcount to 1.
*/
inline AVPacket* MemoryPool::getFreePacket(uint8_t type) {
AVPacket* result = NULL;
if(type == AV_TYPE_VIDEO) {
lockVideo();
{
for(std::vector<AVPacket*>::iterator it = video_packets.begin(); it != video_packets.end(); ++it) {
AVPacket* pkt = *it;
if(pkt->refcount == 0) {
result = pkt;
result->refcount = 1; // WE ADD A REFCOUNT FOR YOU!!
break;
}
}
}
unlockVideo();
}
else if(type == AV_TYPE_AUDIO) {
lockAudio();
{
for(std::vector<AVPacket*>::iterator it = audio_packets.begin(); it != audio_packets.end(); ++it) {
AVPacket* pkt = *it;
if(pkt->refcount == 0) {
result = pkt;
result->refcount = 1; // WE ADD A REFCOUNT FOR YOU!!
break;
}
}
}
unlockAudio();
}
return result;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment